diff --git a/.gitignore b/.gitignore index 66c01b75..066f6918 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ Archive_*/** **/node_modules/** #ignore dist folder -**/dist/** \ No newline at end of file +**/dist/** diff --git a/SerpentRace_Backend/.env.dev b/SerpentRace_Backend/.env.dev new file mode 100644 index 00000000..a4ee74c0 --- /dev/null +++ b/SerpentRace_Backend/.env.dev @@ -0,0 +1,29 @@ +# Development Environment Variables for Local Build +# These are used when running build scripts outside of Docker containers + +NODE_ENV=development +PORT=3000 + +# Database Configuration (Docker containers) +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=serpentrace +DB_USERNAME=postgres +DB_PASSWORD=postgres + +# Redis Configuration (Docker containers) +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_URL=redis://localhost:6379 + +# JWT Configuration +JWT_SECRET=dev_jwt_secret_change_in_production +JWT_EXPIRATION=24h +JWT_REFRESH_EXPIRATION=7d + +# MinIO Configuration (Docker containers) +MINIO_ENDPOINT=localhost +MINIO_PORT=9000 +MINIO_ACCESS_KEY=serpentrace +MINIO_SECRET_KEY=serpentrace123! +MINIO_USE_SSL=false diff --git a/SerpentRace_Backend/.gitignore b/SerpentRace_Backend/.gitignore index 3df116f3..c8d513b6 100644 --- a/SerpentRace_Backend/.gitignore +++ b/SerpentRace_Backend/.gitignore @@ -2,4 +2,4 @@ ./node_modules/* ./Archive_*/* ./Archive_* -./logs/* \ No newline at end of file +./logs/* diff --git a/SerpentRace_Backend/dist/Api/index.d.ts b/SerpentRace_Backend/dist/Api/index.d.ts new file mode 100644 index 00000000..1108984e --- /dev/null +++ b/SerpentRace_Backend/dist/Api/index.d.ts @@ -0,0 +1,4 @@ +import { WebSocketService } from '../Application/Services/WebSocketService'; +declare let webSocketService: WebSocketService; +export { webSocketService }; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/index.d.ts.map b/SerpentRace_Backend/dist/Api/index.d.ts.map new file mode 100644 index 00000000..a93618ad --- /dev/null +++ b/SerpentRace_Backend/dist/Api/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Api/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAmJ5E,QAAA,IAAI,gBAAgB,EAAE,gBAAgB,CAAC;AAyFvC,OAAO,EAAE,gBAAgB,EAAE,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/index.js b/SerpentRace_Backend/dist/Api/index.js new file mode 100644 index 00000000..3713015c --- /dev/null +++ b/SerpentRace_Backend/dist/Api/index.js @@ -0,0 +1,225 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.webSocketService = void 0; +const express_1 = __importDefault(require("express")); +const http_1 = require("http"); +const cookie_parser_1 = __importDefault(require("cookie-parser")); +const helmet_1 = __importDefault(require("helmet")); +const ormconfig_1 = require("../Infrastructure/ormconfig"); +const userRouter_1 = __importDefault(require("./routers/userRouter")); +const organizationRouter_1 = __importDefault(require("./routers/organizationRouter")); +const deckRouter_1 = __importDefault(require("./routers/deckRouter")); +const chatRouter_1 = __importDefault(require("./routers/chatRouter")); +const contactRouter_1 = __importDefault(require("./routers/contactRouter")); +const adminRouter_1 = __importDefault(require("./routers/adminRouter")); +const deckImportExportRouter_1 = __importDefault(require("./routers/deckImportExportRouter")); +const Logger_1 = require("../Application/Services/Logger"); +const WebSocketService_1 = require("../Application/Services/WebSocketService"); +const swaggerUiSetup_1 = require("./swagger/swaggerUiSetup"); +const app = (0, express_1.default)(); +const httpServer = (0, http_1.createServer)(app); +const PORT = process.env.PORT || 3000; +const isDevelopment = process.env.NODE_ENV === 'development'; +const loggingService = Logger_1.LoggingService.getInstance(); +(0, Logger_1.logStartup)('SerpentRace Backend starting up', { + environment: process.env.NODE_ENV || 'development', + port: PORT, + nodeVersion: process.version, + chatInactivityTimeout: process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30' +}); +app.use((0, helmet_1.default)({ + contentSecurityPolicy: isDevelopment ? false : undefined +})); +app.use(express_1.default.json({ limit: '10mb' })); +app.use(express_1.default.urlencoded({ extended: true, limit: '10mb' })); +app.use((0, cookie_parser_1.default)()); +app.use(loggingService.requestLoggingMiddleware()); +app.use((req, res, next) => { + const origin = req.headers.origin; + const allowedOrigins = ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:8080']; + if (!origin || allowedOrigins.includes(origin)) { + res.setHeader('Access-Control-Allow-Origin', origin || '*'); + } + res.setHeader('Access-Control-Allow-Credentials', 'true'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Cookie'); + if (req.method === 'OPTIONS') { + res.status(200).end(); + return; + } + next(); +}); +if (isDevelopment) { + app.use((req, res, next) => { + (0, Logger_1.logRequest)(`${req.method} ${req.path}`, req, res); + next(); + }); +} +// Setup Swagger documentation +(0, swaggerUiSetup_1.setupSwagger)(app); +app.get('/', (req, res) => { + res.json({ + service: 'SerpentRace Backend API', + status: 'running', + version: '1.0.0', + endpoints: { + swagger: '/api-docs', + users: '/api/users', + organizations: '/api/organizations', + decks: '/api/decks', + chats: '/api/chats', + contacts: '/api/contacts', + admin: '/api/admin', + deckImportExport: '/api/deck-import-export', + health: '/health' + }, + websocket: { + enabled: true, + events: [ + 'chat:join', 'chat:leave', 'message:send', + 'group:create', 'chat:direct', 'game:chat:create', + 'chat:history' + ] + } + }); +}); +app.get('/health', async (req, res) => { + try { + const isDbConnected = ormconfig_1.AppDataSource.isInitialized; + res.json({ + status: 'healthy', + timestamp: new Date().toISOString(), + service: 'SerpentRace Backend API', + version: '1.0.0', + environment: process.env.NODE_ENV || 'development', + database: { + connected: isDbConnected, + type: ormconfig_1.AppDataSource.options.type + }, + websocket: { + enabled: true + }, + uptime: process.uptime() + }); + } + catch (error) { + res.status(503).json({ + status: 'unhealthy', + timestamp: new Date().toISOString(), + error: 'Service health check failed' + }); + } +}); +// API Routes +app.use('/api/users', userRouter_1.default); +app.use('/api/organizations', organizationRouter_1.default); +app.use('/api/decks', deckRouter_1.default); +app.use('/api/chats', chatRouter_1.default); +app.use('/api/contacts', contactRouter_1.default); +app.use('/api/admin', adminRouter_1.default); +app.use('/api/deck-import-export', deckImportExportRouter_1.default); +// Global error handler (must be after routes) +app.use(loggingService.errorLoggingMiddleware()); +app.use((error, req, res, next) => { + (0, Logger_1.logError)('Global error handler caught unhandled error', error, req, res); + // Don't expose internal error details in production + const isDevelopment = process.env.NODE_ENV === 'development'; + res.status(500).json({ + error: 'Internal server error', + timestamp: new Date().toISOString(), + ...(isDevelopment && { details: error.message, stack: error.stack }) + }); +}); +// Handle 404 routes +app.use((req, res) => { + res.status(404).json({ + error: 'Route not found', + path: req.originalUrl, + method: req.method, + timestamp: new Date().toISOString() + }); +}); +// Initialize WebSocket service after database connection +let webSocketService; +// Initialize database connection +ormconfig_1.AppDataSource.initialize() + .then(() => { + const dbOptions = ormconfig_1.AppDataSource.options; + (0, Logger_1.logConnection)('Database connection established', 'postgresql', 'success', { + type: dbOptions.type, + host: dbOptions.host, + database: dbOptions.database + }); + // Initialize WebSocket service after database is connected + exports.webSocketService = webSocketService = new WebSocketService_1.WebSocketService(httpServer); + (0, Logger_1.logStartup)('WebSocket service initialized', { + chatInactivityTimeout: process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30' + }); +}) + .catch((error) => { + const dbOptions = ormconfig_1.AppDataSource.options; + (0, Logger_1.logConnection)('Database connection failed', 'postgresql', 'failure', { + error: error.message, + type: dbOptions.type, + host: dbOptions.host, + database: dbOptions.database + }); + process.exit(1); +}); +// Start server with WebSocket support +const server = httpServer.listen(PORT, () => { + (0, Logger_1.logStartup)('Server started successfully', { + port: PORT, + environment: process.env.NODE_ENV || 'development', + timestamp: new Date().toISOString(), + endpoints: { + health: `/health`, + swagger: `/api-docs`, + users: `/api/users`, + organizations: `/api/organizations`, + decks: `/api/decks`, + chats: `/api/chats` + }, + websocket: { + enabled: true, + chatInactivityTimeout: `${process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30'} minutes` + } + }); +}); +// Graceful shutdown +const gracefulShutdown = async (signal) => { + (0, Logger_1.logStartup)(`Received ${signal}. Shutting down gracefully...`); + server.close(() => { + (0, Logger_1.logStartup)('HTTP server closed'); + if (ormconfig_1.AppDataSource.isInitialized) { + ormconfig_1.AppDataSource.destroy() + .then(() => { + (0, Logger_1.logConnection)('Database connection closed', 'postgresql', 'success'); + process.exit(0); + }) + .catch((error) => { + (0, Logger_1.logError)('Error during database shutdown', error); + process.exit(1); + }); + } + else { + process.exit(0); + } + }); +}; +process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); +process.on('SIGINT', () => gracefulShutdown('SIGINT')); +// Handle uncaught exceptions +process.on('uncaughtException', (error) => { + (0, Logger_1.logError)('Uncaught Exception - Server will shut down', error); + process.exit(1); +}); +// Handle unhandled promise rejections +process.on('unhandledRejection', (reason, promise) => { + (0, Logger_1.logError)('Unhandled Rejection - Server will shut down', new Error(String(reason)), undefined, undefined); + process.exit(1); +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/index.js.map b/SerpentRace_Backend/dist/Api/index.js.map new file mode 100644 index 00000000..3c806547 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/Api/index.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA8B;AAC9B,+BAAoC;AACpC,kEAAyC;AACzC,oDAA4B;AAC5B,2DAA4D;AAC5D,sEAA8C;AAC9C,sFAA8D;AAC9D,sEAA8C;AAC9C,sEAA8C;AAC9C,4EAAoD;AACpD,wEAAgD;AAChD,8FAAsE;AACtE,2DAAiH;AACjH,+EAA4E;AAC5E,6DAAwD;AAExD,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AACtB,MAAM,UAAU,GAAG,IAAA,mBAAY,EAAC,GAAG,CAAC,CAAC;AACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AACtC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;AAE7D,MAAM,cAAc,GAAG,uBAAc,CAAC,WAAW,EAAE,CAAC;AAEpD,IAAA,mBAAU,EAAC,iCAAiC,EAAE;IAC5C,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;IAClD,IAAI,EAAE,IAAI;IACV,WAAW,EAAE,OAAO,CAAC,OAAO;IAC5B,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,IAAI;CAC3E,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,IAAA,gBAAM,EAAC;IACX,qBAAqB,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;CAC3D,CAAC,CAAC,CAAC;AAEJ,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AACzC,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAC/D,GAAG,CAAC,GAAG,CAAC,IAAA,uBAAY,GAAE,CAAC,CAAC;AAExB,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,wBAAwB,EAAE,CAAC,CAAC;AAEnD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,MAAM,cAAc,GAAG,CAAC,uBAAuB,EAAE,uBAAuB,EAAE,uBAAuB,CAAC,CAAC;IAEnG,IAAI,CAAC,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;IAChE,CAAC;IAED,GAAG,CAAC,SAAS,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;IAC1D,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,wCAAwC,CAAC,CAAC;IACxF,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,qCAAqC,CAAC,CAAC;IAErF,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC3B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QACtB,OAAO;IACX,CAAC;IAED,IAAI,EAAE,CAAC;AACX,CAAC,CAAC,CAAC;AAEH,IAAI,aAAa,EAAE,CAAC;IAChB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACvB,IAAA,mBAAU,EAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAClD,IAAI,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;AACP,CAAC;AAED,8BAA8B;AAC9B,IAAA,6BAAY,EAAC,GAAG,CAAC,CAAC;AAElB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACxB,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,yBAAyB;QAClC,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE;YACT,OAAO,EAAE,WAAW;YACpB,KAAK,EAAE,YAAY;YACnB,aAAa,EAAE,oBAAoB;YACnC,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,YAAY;YACnB,gBAAgB,EAAE,yBAAyB;YAC3C,MAAM,EAAE,SAAS;SAClB;QACD,SAAS,EAAE;YACT,OAAO,EAAE,IAAI;YACb,MAAM,EAAE;gBACN,WAAW,EAAE,YAAY,EAAE,cAAc;gBACzC,cAAc,EAAE,aAAa,EAAE,kBAAkB;gBACjD,cAAc;aACf;SACF;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACpC,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,yBAAa,CAAC,aAAa,CAAC;QAElD,GAAG,CAAC,IAAI,CAAC;YACP,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE,yBAAyB;YAClC,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;YAClD,QAAQ,EAAE;gBACR,SAAS,EAAE,aAAa;gBACxB,IAAI,EAAE,yBAAa,CAAC,OAAO,CAAC,IAAI;aACjC;YACD,SAAS,EAAE;gBACT,OAAO,EAAE,IAAI;aACd;YACD,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,MAAM,EAAE,WAAW;YACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,KAAK,EAAE,6BAA6B;SACrC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,aAAa;AACb,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,oBAAU,CAAC,CAAC;AAClC,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,4BAAkB,CAAC,CAAC;AAClD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,oBAAU,CAAC,CAAC;AAClC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,oBAAU,CAAC,CAAC;AAClC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,uBAAa,CAAC,CAAC;AACxC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,qBAAW,CAAC,CAAC;AACnC,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,gCAAsB,CAAC,CAAC;AAE3D,8CAA8C;AAC9C,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,sBAAsB,EAAE,CAAC,CAAC;AACjD,GAAG,CAAC,GAAG,CAAC,CAAC,KAAY,EAAE,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAE,EAAE;IAChG,IAAA,iBAAQ,EAAC,6CAA6C,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAEzE,oDAAoD;IACpD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;IAE7D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACnB,KAAK,EAAE,uBAAuB;QAC9B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,GAAG,CAAC,aAAa,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;KACrE,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,oBAAoB;AACpB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAoB,EAAE,GAAqB,EAAE,EAAE;IACtD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACnB,KAAK,EAAE,iBAAiB;QACxB,IAAI,EAAE,GAAG,CAAC,WAAW;QACrB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,yDAAyD;AACzD,IAAI,gBAAkC,CAAC;AAEvC,iCAAiC;AACjC,yBAAa,CAAC,UAAU,EAAE;KACrB,IAAI,CAAC,GAAG,EAAE;IACP,MAAM,SAAS,GAAG,yBAAa,CAAC,OAAc,CAAC;IAC/C,IAAA,sBAAa,EAAC,iCAAiC,EAAE,YAAY,EAAE,SAAS,EAAE;QACtE,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,QAAQ,EAAE,SAAS,CAAC,QAAQ;KAC/B,CAAC,CAAC;IAEH,2DAA2D;IAC3D,2BAAA,gBAAgB,GAAG,IAAI,mCAAgB,CAAC,UAAU,CAAC,CAAC;IACpD,IAAA,mBAAU,EAAC,+BAA+B,EAAE;QACxC,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,IAAI;KAC7E,CAAC,CAAC;AACP,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACb,MAAM,SAAS,GAAG,yBAAa,CAAC,OAAc,CAAC;IAC/C,IAAA,sBAAa,EAAC,4BAA4B,EAAE,YAAY,EAAE,SAAS,EAAE;QACjE,KAAK,EAAE,KAAK,CAAC,OAAO;QACpB,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,QAAQ,EAAE,SAAS,CAAC,QAAQ;KAC/B,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC;AAEP,sCAAsC;AACtC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IAC1C,IAAA,mBAAU,EAAC,6BAA6B,EAAE;QACxC,IAAI,EAAE,IAAI;QACV,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;QAClD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,SAAS,EAAE;YACT,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,WAAW;YACpB,KAAK,EAAE,YAAY;YACnB,aAAa,EAAE,oBAAoB;YACnC,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,YAAY;SACpB;QACD,SAAS,EAAE;YACT,OAAO,EAAE,IAAI;YACb,qBAAqB,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,IAAI,UAAU;SACxF;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,oBAAoB;AACpB,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;IAChD,IAAA,mBAAU,EAAC,YAAY,MAAM,+BAA+B,CAAC,CAAC;IAE9D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;QAChB,IAAA,mBAAU,EAAC,oBAAoB,CAAC,CAAC;QAEjC,IAAI,yBAAa,CAAC,aAAa,EAAE,CAAC;YAChC,yBAAa,CAAC,OAAO,EAAE;iBACpB,IAAI,CAAC,GAAG,EAAE;gBACT,IAAA,sBAAa,EAAC,4BAA4B,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;gBACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACf,IAAA,iBAAQ,EAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;gBAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;QACP,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACzD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEvD,6BAA6B;AAC7B,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE;IACxC,IAAA,iBAAQ,EAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,sCAAsC;AACtC,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;IACnD,IAAA,iBAAQ,EAAC,6CAA6C,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACzG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/adminRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/adminRouter.d.ts new file mode 100644 index 00000000..58a39b8e --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/adminRouter.d.ts @@ -0,0 +1,10 @@ +declare global { + namespace Express { + interface Request { + file?: Express.Multer.File; + } + } +} +declare const router: import("express-serve-static-core").Router; +export default router; +//# sourceMappingURL=adminRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/adminRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/adminRouter.d.ts.map new file mode 100644 index 00000000..1782b0b7 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/adminRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"adminRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/adminRouter.ts"],"names":[],"mappings":"AAYA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC;QACd,UAAU,OAAO;YACb,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;SAC9B;KACJ;CACJ;AAED,QAAA,MAAM,MAAM,4CAAmB,CAAC;AA4kChC,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/adminRouter.js b/SerpentRace_Backend/dist/Api/routers/adminRouter.js new file mode 100644 index 00000000..d7e006da --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/adminRouter.js @@ -0,0 +1,932 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = __importDefault(require("express")); +const multer_1 = __importDefault(require("multer")); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const AuthMiddleware_1 = require("../../Application/Services/AuthMiddleware"); +const ValidationMiddleware_1 = require("../../Application/Services/ValidationMiddleware"); +const AdminBypassService_1 = require("../../Application/Services/AdminBypassService"); +const Logger_1 = require("../../Application/Services/Logger"); +const router = express_1.default.Router(); +const container = DIContainer_1.DIContainer.getInstance(); +// Configure multer for file uploads +const upload = (0, multer_1.default)({ + storage: multer_1.default.memoryStorage(), + limits: { + fileSize: 10 * 1024 * 1024, // 10MB limit + }, + fileFilter: (req, file, cb) => { + if (file.mimetype === 'application/json' || file.originalname.endsWith('.spr')) { + cb(null, true); + } + else { + cb(new Error('Only JSON and .spr files are allowed')); + } + } +}); +// Helper function to extract language from Accept-Language header +function extractLanguageFromAcceptHeader(acceptLanguage) { + if (!acceptLanguage) + return null; + const languages = acceptLanguage.split(','); + if (languages.length > 0) { + const primaryLanguage = languages[0].split(';')[0].trim().substring(0, 2); + return primaryLanguage; + } + return null; +} +// ============================================================================= +// USER MANAGEMENT ROUTES +// ============================================================================= +// Get users with pagination (RECOMMENDED) +router.get('/users/page/:from/:to', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ + error: 'Invalid pagination parameters. From and to must be valid numbers with from <= to.' + }); + } + const limit = to - from + 1; + if (limit > 100) { + return res.status(400).json({ + error: 'Page size too large. Maximum 100 records per request.' + }); + } + (0, Logger_1.logRequest)('Admin paginated users endpoint accessed', req, res, { from, to, includeDeleted }); + const result = await container.getUsersByPageQueryHandler.execute({ + from, + to, + includeDeleted + }); + const response = { + users: result.users, + pagination: { + from, + to, + returned: result.users.length, + totalCount: result.totalCount, + includeDeleted + } + }; + (0, Logger_1.logRequest)('Admin users retrieved successfully', req, res, { + returnedUsers: result.users.length, + totalCount: result.totalCount, + from, + to, + includeDeleted + }); + return res.status(200).json(response); + } + catch (error) { + (0, Logger_1.logError)('Error in admin get users endpoint', error, req, res); + return res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get users by page (admin only) - RECOMMENDED +router.get('/users/page/:from/:to', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + (0, Logger_1.logRequest)('Admin get users by page endpoint accessed', req, res, { from, to, includeDeleted }); + const result = includeDeleted + ? await container.userRepository.findByPageIncludingDeleted(from, to) + : await container.userRepository.findByPage(from, to); + (0, Logger_1.logRequest)('Admin users page retrieved successfully', req, res, { + from, + to, + count: result.users.length, + total: result.totalCount, + includeDeleted + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin get users by page endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get user by ID including soft-deleted ones +router.get('/users/:userId', AuthMiddleware_1.adminRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['userId']), async (req, res) => { + try { + const targetUserId = req.params.userId; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin get user by id endpoint accessed', req, res, { targetUserId, includeDeleted }); + const user = includeDeleted + ? await container.userRepository.findByIdIncludingDeleted(targetUserId) + : await container.userRepository.findById(targetUserId); + if (!user) { + (0, Logger_1.logWarning)('User not found', { targetUserId, includeDeleted }, req, res); + return res.status(404).json({ error: 'User not found' }); + } + (0, Logger_1.logRequest)('Admin user retrieved successfully', req, res, { + targetUserId, + username: user.username, + includeDeleted + }); + res.json(user); + } + catch (error) { + (0, Logger_1.logError)('Admin get user by id endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Search users including soft-deleted ones +router.get('/users/search/:searchTerm', AuthMiddleware_1.adminRequired, ValidationMiddleware_1.ValidationMiddleware.validateStringLength({ searchTerm: { min: 2, max: 100 } }), async (req, res) => { + try { + const { searchTerm } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin search users endpoint accessed', req, res, { searchTerm, includeDeleted }); + const users = includeDeleted + ? await container.userRepository.searchIncludingDeleted(searchTerm) + : await container.userRepository.search(searchTerm); + (0, Logger_1.logRequest)('Admin user search completed', req, res, { + searchTerm, + resultCount: Array.isArray(users) ? users.length : (users.totalCount || 0), + includeDeleted + }); + res.json(users); + } + catch (error) { + (0, Logger_1.logError)('Admin search users endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Update any user (admin only) +router.patch('/users/:userId', AuthMiddleware_1.adminRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['userId']), async (req, res) => { + try { + const targetUserId = req.params.userId; + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Admin update user endpoint accessed', req, res, { + adminUserId, + targetUserId, + fieldsToUpdate: Object.keys(req.body) + }); + const result = await container.updateUserCommandHandler.execute({ id: targetUserId, ...req.body }); + if (!result) { + return res.status(404).json({ error: 'User not found' }); + } + (0, Logger_1.logRequest)('User updated by admin', req, res, { + adminUserId, + targetUserId, + username: result.username + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin update user endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('already exists')) { + return res.status(409).json({ error: error.message }); + } + if (error.message.includes('validation')) { + return res.status(400).json({ error: error.message }); + } + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Deactivate user (admin only) +router.post('/users/:userId/deactivate', AuthMiddleware_1.adminRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['userId']), async (req, res) => { + try { + const targetUserId = req.params.userId; + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Deactivate user endpoint accessed', req, res, { adminUserId, targetUserId }); + const result = await container.deactivateUserCommandHandler.execute({ id: targetUserId }); + if (!result) { + return res.status(404).json({ error: 'User not found' }); + } + (0, Logger_1.logAuth)('User deactivated by admin', targetUserId, { adminUserId }, req, res); + res.json({ message: 'User deactivated successfully', user: result }); + } + catch (error) { + (0, Logger_1.logError)('Deactivate user endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Delete user (admin only) +router.delete('/users/:userId', AuthMiddleware_1.adminRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['userId']), async (req, res) => { + try { + const targetUserId = req.params.userId; + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Delete user endpoint accessed', req, res, { adminUserId, targetUserId }); + const result = await container.deleteUserCommandHandler.execute({ id: targetUserId }); + if (!result) { + return res.status(404).json({ error: 'User not found' }); + } + (0, Logger_1.logAuth)('User deleted by admin', targetUserId, { adminUserId }, req, res); + res.json({ message: 'User deleted successfully' }); + } + catch (error) { + (0, Logger_1.logError)('Delete user endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// ============================================================================= +// DECK MANAGEMENT ROUTES +// ============================================================================= +// Get decks by page (admin only) - RECOMMENDED +router.get('/decks/page/:from/:to', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + (0, Logger_1.logRequest)('Admin get decks by page endpoint accessed', req, res, { from, to, includeDeleted }); + // For admin, we need to pass admin context to get unrestricted decks + const adminUserId = req.user.userId; + const result = await container.getDecksByPageQueryHandler.execute({ + userId: adminUserId, + userOrgId: undefined, + isAdmin: true, + from, + to, + includeDeleted + }); + (0, Logger_1.logRequest)('Admin decks page retrieved successfully', req, res, { + from, + to, + count: result.decks.length, + total: result.totalCount, + includeDeleted + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin get decks by page endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get deck by ID including soft-deleted ones +router.get('/decks/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const { id } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin get deck by id endpoint accessed', req, res, { deckId: id, includeDeleted }); + const deck = includeDeleted + ? await container.deckRepository.findByIdIncludingDeleted(id) + : await container.deckRepository.findById(id); + if (!deck) { + (0, Logger_1.logWarning)('Deck not found', { deckId: id, includeDeleted }, req, res); + return res.status(404).json({ error: 'Deck not found' }); + } + (0, Logger_1.logRequest)('Admin deck retrieved successfully', req, res, { deckId: id, includeDeleted }); + res.json(deck); + } + catch (error) { + (0, Logger_1.logError)('Admin get deck by id endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Search decks including soft-deleted ones +router.get('/decks/search/:searchTerm', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const { searchTerm } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin search decks endpoint accessed', req, res, { searchTerm, includeDeleted }); + const decks = includeDeleted + ? await container.deckRepository.searchIncludingDeleted(searchTerm) + : await container.deckRepository.search(searchTerm); + (0, Logger_1.logRequest)('Admin deck search completed', req, res, { + searchTerm, + resultCount: Array.isArray(decks) ? decks.length : (decks.totalCount || 0), + includeDeleted + }); + res.json(decks); + } + catch (error) { + (0, Logger_1.logError)('Admin search decks endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Hard delete deck (admin only) +router.delete('/decks/:id/hard', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const deckId = req.params.id; + (0, Logger_1.logRequest)('Admin hard delete deck endpoint accessed', req, res, { deckId }); + const result = await container.deleteDeckCommandHandler.execute({ id: deckId, soft: false }); + (0, Logger_1.logRequest)('Admin deck hard delete successful', req, res, { deckId, success: result }); + res.json({ success: result }); + } + catch (error) { + (0, Logger_1.logError)('Admin hard delete deck endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Deck not found' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// ============================================================================= +// ORGANIZATION MANAGEMENT ROUTES +// ============================================================================= +// Create organization (admin only) +router.post('/organizations', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Admin create organization endpoint accessed', req, res, { name: req.body.name, adminUserId }); + const result = await container.createOrganizationCommandHandler.execute(req.body); + AdminBypassService_1.AdminAuditService.logAdminAction('CREATE_ORGANIZATION', adminUserId, { + targetType: 'organization', + targetId: result.id, + operation: 'create', + changes: req.body + }, req, res); + (0, Logger_1.logRequest)('Admin organization created successfully', req, res, { organizationId: result.id, name: req.body.name, adminUserId }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin create organization endpoint error', error, req, res); + if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique constraint'))) { + return res.status(409).json({ error: 'Organization with this name already exists' }); + } + if (error instanceof Error && error.message.includes('validation')) { + return res.status(400).json({ error: 'Invalid input data', details: error.message }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Update organization (admin only) - NEW ENDPOINT +router.patch('/organizations/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const organizationId = req.params.id; + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Admin update organization endpoint accessed', req, res, { + adminUserId, + organizationId, + fieldsToUpdate: Object.keys(req.body) + }); + const result = await container.updateOrganizationCommandHandler.execute({ + id: organizationId, + ...req.body + }); + if (!result) { + return res.status(404).json({ error: 'Organization not found' }); + } + AdminBypassService_1.AdminAuditService.logAdminAction('UPDATE_ORGANIZATION', adminUserId, { + targetType: 'organization', + targetId: organizationId, + operation: 'update', + changes: req.body, + sensitive: req.body.maxOrganizationalDecks !== undefined + }, req, res); + (0, Logger_1.logRequest)('Organization updated by admin', req, res, { + adminUserId, + organizationId, + organizationName: result.name + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin update organization endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('already exists')) { + return res.status(409).json({ error: error.message }); + } + if (error.message.includes('validation')) { + return res.status(400).json({ error: error.message }); + } + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get organizations by page (admin only) - RECOMMENDED +router.get('/organizations/page/:from/:to', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + (0, Logger_1.logRequest)('Admin get organizations by page endpoint accessed', req, res, { from, to, includeDeleted }); + const result = await container.getOrganizationsByPageQueryHandler.execute({ + from, + to, + includeDeleted + }); + (0, Logger_1.logRequest)('Admin organizations page retrieved successfully', req, res, { + from, + to, + count: result.organizations.length, + total: result.totalCount, + includeDeleted + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin get organizations by page endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get organization by ID including soft-deleted ones +router.get('/organizations/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const organizationId = req.params.id; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin get organization by id endpoint accessed', req, res, { organizationId, includeDeleted }); + const organization = includeDeleted + ? await container.organizationRepository.findByIdIncludingDeleted(organizationId) + : await container.organizationRepository.findById(organizationId); + if (!organization) { + (0, Logger_1.logWarning)('Organization not found', { organizationId, includeDeleted }, req, res); + return res.status(404).json({ error: 'Organization not found' }); + } + (0, Logger_1.logRequest)('Admin organization retrieved successfully', req, res, { organizationId, includeDeleted }); + res.json(organization); + } + catch (error) { + (0, Logger_1.logError)('Admin get organization by id endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Search organizations including soft-deleted ones +router.get('/organizations/search/:searchTerm', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const { searchTerm } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin search organizations endpoint accessed', req, res, { searchTerm, includeDeleted }); + const organizations = includeDeleted + ? await container.organizationRepository.searchIncludingDeleted(searchTerm) + : await container.organizationRepository.search(searchTerm); + (0, Logger_1.logRequest)('Admin organization search completed', req, res, { + searchTerm, + resultCount: Array.isArray(organizations) ? organizations.length : (organizations.totalCount || 0), + includeDeleted + }); + res.json(organizations); + } + catch (error) { + (0, Logger_1.logError)('Admin search organizations endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Soft delete organization (admin only) +router.delete('/organizations/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const organizationId = req.params.id; + (0, Logger_1.logRequest)('Admin soft delete organization endpoint accessed', req, res, { organizationId }); + const result = await container.deleteOrganizationCommandHandler.execute({ id: organizationId, soft: true }); + (0, Logger_1.logRequest)('Admin organization soft delete successful', req, res, { organizationId, success: result }); + res.json({ success: result }); + } + catch (error) { + (0, Logger_1.logError)('Admin soft delete organization endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Organization not found' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Hard delete organization (admin only) +router.delete('/organizations/:id/hard', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const organizationId = req.params.id; + (0, Logger_1.logRequest)('Admin hard delete organization endpoint accessed', req, res, { organizationId }); + const result = await container.deleteOrganizationCommandHandler.execute({ id: organizationId, soft: false }); + (0, Logger_1.logRequest)('Admin organization hard delete successful', req, res, { organizationId, success: result }); + res.json({ success: result }); + } + catch (error) { + (0, Logger_1.logError)('Admin hard delete organization endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Organization not found' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// ============================================================================= +// CHAT MANAGEMENT ROUTES +// ============================================================================= +// Get chats with pagination (RECOMMENDED) +router.get('/chats/page/:from/:to', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ + error: 'Invalid pagination parameters. From and to must be valid numbers with from <= to.' + }); + } + const limit = to - from + 1; + if (limit > 100) { + return res.status(400).json({ + error: 'Page size too large. Maximum 100 records per request.' + }); + } + (0, Logger_1.logRequest)('Admin paginated chats endpoint accessed', req, res, { from, to, includeDeleted }); + const result = await container.getChatsByPageQueryHandler.execute({ + from, + to, + includeDeleted + }); + const response = { + chats: result.chats, + pagination: { + from, + to, + returned: result.chats.length, + totalCount: result.totalCount, + includeDeleted + } + }; + (0, Logger_1.logRequest)('Admin chats retrieved successfully', req, res, { + returnedChats: result.chats.length, + totalCount: result.totalCount, + from, + to, + includeDeleted + }); + return res.status(200).json(response); + } + catch (error) { + (0, Logger_1.logError)('Error in admin get chats endpoint', error, req, res); + return res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get chat by ID including soft-deleted ones +router.get('/chats/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const { id } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin get chat by id endpoint accessed', req, res, { chatId: id, includeDeleted }); + const chat = includeDeleted + ? await container.chatRepository.findByIdIncludingDeleted(id) + : await container.chatRepository.findById(id); + if (!chat) { + (0, Logger_1.logWarning)('Chat not found', { chatId: id, includeDeleted }, req, res); + return res.status(404).json({ error: 'Chat not found' }); + } + (0, Logger_1.logRequest)('Admin chat retrieved successfully', req, res, { chatId: id, includeDeleted }); + res.json(chat); + } + catch (error) { + (0, Logger_1.logError)('Admin get chat by id endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// ============================================================================= +// CONTACT MANAGEMENT ROUTES +// ============================================================================= +// Get contacts by page (admin only) - RECOMMENDED (already exists, enhanced) +router.get('/contacts/page/:from/:to', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + (0, Logger_1.logRequest)('Admin get contacts by page endpoint accessed', req, res, { from, to, includeDeleted }); + const result = includeDeleted + ? await container.contactRepository.findByPageIncludingDeleted(from, to) + : await container.contactRepository.findByPage(from, to); + (0, Logger_1.logRequest)('Admin contacts page retrieved successfully', req, res, { + from, + to, + count: result.contacts.length, + total: result.totalCount, + includeDeleted + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin get contacts by page endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get contact by ID (admin only) +router.get('/contacts/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const contactId = req.params.id; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin get contact by ID endpoint accessed', req, res, { contactId, includeDeleted }); + const result = includeDeleted + ? await container.contactRepository.findByIdIncludingDeleted(contactId) + : await container.getContactByIdQueryHandler.execute({ id: contactId }); + if (!result) { + (0, Logger_1.logRequest)('Contact not found', req, res, { contactId, includeDeleted }); + return res.status(404).json({ error: 'Contact not found' }); + } + (0, Logger_1.logRequest)('Admin contact retrieved successfully', req, res, { contactId, includeDeleted }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Admin get contact by ID endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Search contacts including soft-deleted ones (admin only) +router.get('/contacts/search/:searchTerm', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const { searchTerm } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + (0, Logger_1.logRequest)('Admin search contacts endpoint accessed', req, res, { searchTerm, includeDeleted }); + const contacts = includeDeleted + ? await container.contactRepository.searchIncludingDeleted(searchTerm) + : await container.contactRepository.search(searchTerm); + (0, Logger_1.logRequest)('Admin contact search completed', req, res, { + searchTerm, + resultCount: contacts.length, + includeDeleted + }); + res.json(contacts); + } + catch (error) { + (0, Logger_1.logError)('Admin search contacts endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Respond to contact (admin only) +router.put('/contacts/:id/respond', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const contactId = req.params.id; + const adminUserId = req.user.userId; + const { adminResponse, sendEmail, language } = req.body; + if (!adminResponse) { + return res.status(400).json({ error: 'Admin response is required' }); + } + // Determine language from body, headers, or default to English + let selectedLanguage = language; + if (!selectedLanguage) { + // Try to get language from Accept-Language header + const acceptLanguage = req.headers['accept-language']; + // Try to get language from custom headers (common frontend patterns) + const regionHeader = req.headers['x-region']; + const languageHeader = req.headers['x-language']; + const localeHeader = req.headers['x-locale']; + selectedLanguage = languageHeader || + localeHeader || + regionHeader || + extractLanguageFromAcceptHeader(acceptLanguage) || + 'en'; + } + // Validate and normalize language parameter + if (!['en', 'hu', 'de'].includes(selectedLanguage.toLowerCase())) { + selectedLanguage = 'en'; // Fallback to English for unsupported languages + } + else { + selectedLanguage = selectedLanguage.toLowerCase(); + } + (0, Logger_1.logRequest)('Admin respond to contact endpoint accessed', req, res, { + contactId, + adminUserId, + sendEmail, + language: selectedLanguage, + headerLanguage: req.headers['accept-language'] || req.headers['x-language'] || 'none' + }); + // Update contact with response + const result = await container.updateContactCommandHandler.execute({ + id: contactId, + adminResponse, + respondedBy: adminUserId + }); + if (!result) { + (0, Logger_1.logWarning)('Contact not found for response', { contactId }, req, res); + return res.status(404).json({ error: 'Contact not found' }); + } + // Send email if requested + let emailSent = false; + let emailError = null; + if (sendEmail === true && adminResponse) { + try { + await container.contactEmailService.sendResponse({ + to: result.email, + message: adminResponse, + contactId: contactId, + adminUserId: adminUserId, + contactName: result.name, + contactType: result.type, + originalMessage: result.txt, + language: selectedLanguage + }); + emailSent = true; + (0, Logger_1.logRequest)('Contact response email sent successfully', req, res, { + contactId, + recipientEmail: result.email, + language: selectedLanguage + }); + } + catch (emailErr) { + emailError = emailErr instanceof Error ? emailErr.message : 'Email sending failed'; + (0, Logger_1.logError)('Contact response email failed', emailErr, req, res); + } + } + AdminBypassService_1.AdminAuditService.logAdminAction('RESPOND_TO_CONTACT', adminUserId, { + targetType: 'contact', + targetId: contactId, + operation: 'update', + changes: { adminResponse, sendEmail, language: selectedLanguage }, + metadata: { emailSent, emailError } + }, req, res); + (0, Logger_1.logRequest)('Admin contact response saved successfully', req, res, { + contactId, + sendEmail, + emailSent, + language: selectedLanguage + }); + res.json({ + success: true, + message: 'Response saved successfully', + contact: result, + emailSent, + emailError: emailSent ? null : emailError + }); + } + catch (error) { + (0, Logger_1.logError)('Admin respond to contact endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Contact not found' }); + } + if (error instanceof Error && error.message.includes('validation')) { + return res.status(400).json({ error: 'Invalid input data', details: error.message }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Resend contact email (admin only) - NEW ENDPOINT +router.post('/contacts/:id/resend-email', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const contactId = req.params.id; + const adminUserId = req.user.userId; + const { language } = req.body; + (0, Logger_1.logRequest)('Admin resend contact email endpoint accessed', req, res, { + contactId, + adminUserId, + language + }); + // Get contact details + const contact = await container.getContactByIdQueryHandler.execute({ id: contactId }); + if (!contact) { + return res.status(404).json({ error: 'Contact not found' }); + } + if (!contact.adminResponse) { + return res.status(400).json({ error: 'No admin response found to resend' }); + } + const selectedLanguage = language || 'en'; + try { + await container.contactEmailService.sendResponse({ + to: contact.email, + message: contact.adminResponse, + contactId: contactId, + adminUserId: adminUserId, + contactName: contact.name, + contactType: contact.type, + originalMessage: contact.txt, + language: selectedLanguage + }); + AdminBypassService_1.AdminAuditService.logAdminAction('RESEND_CONTACT_EMAIL', adminUserId, { + targetType: 'contact', + targetId: contactId, + operation: 'create', + metadata: { language: selectedLanguage, action: 'resend' } + }, req, res); + (0, Logger_1.logRequest)('Contact email resent successfully', req, res, { + contactId, + recipientEmail: contact.email, + language: selectedLanguage + }); + res.json({ + success: true, + message: 'Email resent successfully' + }); + } + catch (emailErr) { + (0, Logger_1.logError)('Contact email resend failed', emailErr, req, res); + res.status(500).json({ + error: 'Failed to resend email', + details: emailErr instanceof Error ? emailErr.message : 'Unknown error' + }); + } + } + catch (error) { + (0, Logger_1.logError)('Admin resend contact email endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Soft delete contact (admin only) - NEW ENDPOINT +router.delete('/contacts/:id', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const contactId = req.params.id; + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Admin soft delete contact endpoint accessed', req, res, { contactId, adminUserId }); + const result = await container.deleteContactCommandHandler.execute({ + id: contactId, + hard: false + }); + AdminBypassService_1.AdminAuditService.logAdminAction('SOFT_DELETE_CONTACT', adminUserId, { + targetType: 'contact', + targetId: contactId, + operation: 'update' + }, req, res); + (0, Logger_1.logAuth)('Contact soft deleted by admin', contactId, { adminUserId }, req, res); + res.json({ success: result }); + } + catch (error) { + (0, Logger_1.logError)('Admin soft delete contact endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Contact not found' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Hard delete contact (admin only) - NEW ENDPOINT +router.delete('/contacts/:id/hard', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const contactId = req.params.id; + const adminUserId = req.user.userId; + (0, Logger_1.logRequest)('Admin hard delete contact endpoint accessed', req, res, { contactId, adminUserId }); + const result = await container.deleteContactCommandHandler.execute({ + id: contactId, + hard: true + }); + AdminBypassService_1.AdminAuditService.logAdminAction('HARD_DELETE_CONTACT', adminUserId, { + targetType: 'contact', + targetId: contactId, + operation: 'delete', + sensitive: true + }, req, res); + (0, Logger_1.logAuth)('Contact hard deleted by admin', contactId, { adminUserId }, req, res); + res.json({ success: result }); + } + catch (error) { + (0, Logger_1.logError)('Admin hard delete contact endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Contact not found' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +// ============================================================================= +// DECK IMPORT/EXPORT ROUTES (ADMIN) +// ============================================================================= +// Import deck from JSON file (unencrypted, admin only) +router.post('/decks/import', AuthMiddleware_1.adminRequired, upload.single('file'), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ error: 'No file uploaded' }); + } + const userId = req.user.userId; + const fileContent = req.file.buffer.toString('utf-8'); + (0, Logger_1.logRequest)('Admin deck import from JSON endpoint accessed', req, res, { fileName: req.file.originalname }); + let jsonData; + try { + jsonData = JSON.parse(fileContent); + } + catch (parseError) { + return res.status(400).json({ error: 'Invalid JSON format' }); + } + // For admin import, we need to specify both target user and admin user + // Let's assume the deck will be owned by the admin user doing the import + const result = await container.deckImportExportService.adminImportFromJson(jsonData, userId, userId); + (0, Logger_1.logRequest)('Admin deck import successful', req, res, { deckId: result.id, fileName: req.file.originalname }); + res.json({ + success: true, + message: 'Deck imported successfully', + deckId: result.id + }); + } + catch (error) { + (0, Logger_1.logError)('Admin deck import from JSON error', error, req, res); + if (error instanceof Error && error.message.includes('Invalid')) { + res.status(400).json({ error: 'Invalid deck data structure' }); + } + else { + res.status(500).json({ error: 'Internal server error' }); + } + } +}); +// Export deck as JSON (unencrypted, admin only) +router.get('/decks/:deckId/export', AuthMiddleware_1.adminRequired, async (req, res) => { + try { + const { deckId } = req.params; + (0, Logger_1.logRequest)('Admin deck export as JSON endpoint accessed', req, res, { deckId }); + const deck = await container.deckRepository.findById(deckId); + if (!deck) { + (0, Logger_1.logWarning)('Deck not found for export', { deckId }, req, res); + return res.status(404).json({ error: 'Deck not found' }); + } + (0, Logger_1.logRequest)('Admin deck export successful', req, res, { deckId, deckName: deck.name }); + // Return deck as JSON for admin export + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Content-Disposition', `attachment; filename="${deck.name || 'deck'}.json"`); + res.json(deck); + } + catch (error) { + (0, Logger_1.logError)('Admin deck export as JSON error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +exports.default = router; +//# sourceMappingURL=adminRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/adminRouter.js.map b/SerpentRace_Backend/dist/Api/routers/adminRouter.js.map new file mode 100644 index 00000000..54ea9bdd --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/adminRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"adminRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/adminRouter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/chatRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/chatRouter.d.ts new file mode 100644 index 00000000..8c1ccb21 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/chatRouter.d.ts @@ -0,0 +1,3 @@ +declare const chatRouter: import("express-serve-static-core").Router; +export default chatRouter; +//# sourceMappingURL=chatRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/chatRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/chatRouter.d.ts.map new file mode 100644 index 00000000..27bbdc10 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/chatRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"chatRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/chatRouter.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,UAAU,4CAAmB,CAAC;AAuRpC,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/chatRouter.js b/SerpentRace_Backend/dist/Api/routers/chatRouter.js new file mode 100644 index 00000000..73133194 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/chatRouter.js @@ -0,0 +1,231 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = __importDefault(require("express")); +const AuthMiddleware_1 = require("../../Application/Services/AuthMiddleware"); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const ErrorResponseService_1 = require("../../Application/Services/ErrorResponseService"); +const ValidationMiddleware_1 = require("../../Application/Services/ValidationMiddleware"); +const Logger_1 = require("../../Application/Services/Logger"); +const chatRouter = express_1.default.Router(); +// Get user's chats +chatRouter.get('/user-chats', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + const includeArchived = req.query.includeArchived === 'true'; + (0, Logger_1.logRequest)('Get user chats endpoint accessed', req, res, { userId, includeArchived }); + const chats = await DIContainer_1.container.getUserChatsQueryHandler.execute({ + userId, + includeArchived + }); + (0, Logger_1.logRequest)('User chats retrieved successfully', req, res, { + userId, + chatCount: chats.length + }); + res.json(chats); + } + catch (error) { + (0, Logger_1.logError)('Get user chats endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Get chat history +chatRouter.get('/history/:chatId', AuthMiddleware_1.authRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['chatId']), async (req, res) => { + try { + const userId = req.user.userId; + const chatId = req.params.chatId; + (0, Logger_1.logRequest)('Get chat history endpoint accessed', req, res, { userId, chatId }); + const history = await DIContainer_1.container.getChatHistoryQueryHandler.execute({ + chatId, + userId + }); + if (!history) { + (0, Logger_1.logWarning)('Chat history not found or unauthorized', { userId, chatId }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'Chat not found or unauthorized'); + } + (0, Logger_1.logRequest)('Chat history retrieved successfully', req, res, { + userId, + chatId, + messageCount: history.messages.length, + isArchived: history.isArchived + }); + res.json(history); + } + catch (error) { + (0, Logger_1.logError)('Get chat history endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Create new chat (direct/group) +chatRouter.post('/create', AuthMiddleware_1.authRequired, ValidationMiddleware_1.ValidationMiddleware.combine([ + ValidationMiddleware_1.ValidationMiddleware.validateRequiredFields(['type', 'userIds']), + ValidationMiddleware_1.ValidationMiddleware.validateAllowedValues({ type: ['direct', 'group'] }), + ValidationMiddleware_1.ValidationMiddleware.validateNonEmptyArrays(['userIds']) +]), async (req, res) => { + try { + const userId = req.user.userId; + const { type, name, userIds } = req.body; + (0, Logger_1.logRequest)('Create chat endpoint accessed', req, res, { + userId, + type, + targetUserCount: userIds?.length || 0 + }); + if (type === 'group' && !name?.trim()) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Group name is required'); + } + const chat = await DIContainer_1.container.createChatCommandHandler.execute({ + type, + name: name?.trim(), + createdBy: userId, + userIds + }); + if (!chat) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Failed to create chat'); + } + (0, Logger_1.logRequest)('Chat created successfully', req, res, { + userId, + chatId: chat.id, + chatType: chat.type + }); + res.json({ + id: chat.id, + type: chat.type, + name: chat.name, + users: chat.users, + messages: chat.messages + }); + } + catch (error) { + (0, Logger_1.logError)('Create chat endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('Premium subscription required')) { + return ErrorResponseService_1.ErrorResponseService.sendForbidden(res, 'Premium subscription required to create groups'); + } + if (error.message.includes('not found')) { + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'One or more users not found'); + } + } + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Send message (REST endpoint - mainly for testing, real messaging is via WebSocket) +chatRouter.post('/message', AuthMiddleware_1.authRequired, ValidationMiddleware_1.ValidationMiddleware.combine([ + ValidationMiddleware_1.ValidationMiddleware.validateRequiredFields(['chatId', 'message']), + ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['chatId']), + ValidationMiddleware_1.ValidationMiddleware.validateStringLength({ message: { min: 1, max: 2000 } }) +]), async (req, res) => { + try { + const userId = req.user.userId; + const { chatId, message } = req.body; + (0, Logger_1.logRequest)('Send message endpoint accessed', req, res, { + userId, + chatId, + messageLength: message?.length || 0 + }); + const sentMessage = await DIContainer_1.container.sendMessageCommandHandler.execute({ + chatId, + userId, + message + }); + if (!sentMessage) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Failed to send message'); + } + (0, Logger_1.logRequest)('Message sent successfully', req, res, { + userId, + chatId, + messageId: sentMessage.id + }); + res.json(sentMessage); + } + catch (error) { + (0, Logger_1.logError)('Send message endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('Chat not found')) { + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'Chat not found'); + } + if (error.message.includes('not a member')) { + return ErrorResponseService_1.ErrorResponseService.sendForbidden(res, 'Not authorized to send messages to this chat'); + } + if (error.message.includes('non-empty string')) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Message must be a non-empty string'); + } + } + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Archive chat manually +chatRouter.post('/archive/:chatId', AuthMiddleware_1.authRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['chatId']), async (req, res) => { + try { + const userId = req.user.userId; + const chatId = req.params.chatId; + (0, Logger_1.logRequest)('Archive chat endpoint accessed', req, res, { userId, chatId }); + // Check if user has access to this chat + const chat = await DIContainer_1.container.chatRepository.findById(chatId); + if (!chat) { + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'Chat not found'); + } + if (!chat.users.includes(userId)) { + return ErrorResponseService_1.ErrorResponseService.sendForbidden(res, 'Not authorized to archive this chat'); + } + const success = await DIContainer_1.container.archiveChatCommandHandler.execute({ chatId }); + if (!success) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Failed to archive chat'); + } + (0, Logger_1.logRequest)('Chat archived successfully', req, res, { userId, chatId }); + res.json({ success: true, message: 'Chat archived successfully' }); + } + catch (error) { + (0, Logger_1.logError)('Archive chat endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Restore chat from archive +chatRouter.post('/restore/:chatId', AuthMiddleware_1.authRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['chatId']), async (req, res) => { + try { + const userId = req.user.userId; + const chatId = req.params.chatId; + (0, Logger_1.logRequest)('Restore chat endpoint accessed', req, res, { userId, chatId }); + // Check if user has access to this archived chat + const archive = await DIContainer_1.container.chatArchiveRepository.findByChatId(chatId); + const userArchive = archive.find((a) => a.participants.includes(userId)); + if (!userArchive) { + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'Archived chat not found or unauthorized'); + } + const success = await DIContainer_1.container.restoreChatCommandHandler.execute({ chatId }); + if (!success) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Failed to restore chat (game chats cannot be restored)'); + } + (0, Logger_1.logRequest)('Chat restored successfully', req, res, { userId, chatId }); + res.json({ success: true, message: 'Chat restored successfully' }); + } + catch (error) { + (0, Logger_1.logError)('Restore chat endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Get archived chats for a game +chatRouter.get('/archived/game/:gameId', AuthMiddleware_1.authRequired, ValidationMiddleware_1.ValidationMiddleware.validateUUIDFormat(['gameId']), async (req, res) => { + try { + const userId = req.user.userId; + const gameId = req.params.gameId; + (0, Logger_1.logRequest)('Get archived game chats endpoint accessed', req, res, { userId, gameId }); + const archivedChats = await DIContainer_1.container.getArchivedChatsQueryHandler.execute({ + userId, + gameId + }); + (0, Logger_1.logRequest)('Archived game chats retrieved successfully', req, res, { + userId, + gameId, + chatCount: archivedChats.length + }); + res.json(archivedChats); + } + catch (error) { + (0, Logger_1.logError)('Get archived game chats endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +exports.default = chatRouter; +//# sourceMappingURL=chatRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/chatRouter.js.map b/SerpentRace_Backend/dist/Api/routers/chatRouter.js.map new file mode 100644 index 00000000..00e5fc0e --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/chatRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"chatRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/chatRouter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/contactRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/contactRouter.d.ts new file mode 100644 index 00000000..4383ff22 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/contactRouter.d.ts @@ -0,0 +1,3 @@ +declare const contactRouter: import("express-serve-static-core").Router; +export default contactRouter; +//# sourceMappingURL=contactRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/contactRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/contactRouter.d.ts.map new file mode 100644 index 00000000..b3084169 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/contactRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"contactRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/contactRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAW,CAAC;AA+C/B,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/contactRouter.js b/SerpentRace_Backend/dist/Api/routers/contactRouter.js new file mode 100644 index 00000000..1b0b35e7 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/contactRouter.js @@ -0,0 +1,46 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = require("express"); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const Logger_1 = require("../../Application/Services/Logger"); +const ContactAggregate_1 = require("../../Domain/Contact/ContactAggregate"); +const contactRouter = (0, express_1.Router)(); +// Public endpoint - anyone can create a contact +contactRouter.post('/', async (req, res) => { + try { + // Get user ID if authenticated (optional) + const userId = req.user?.userId || null; + const { name, email, type, txt } = req.body; + // Validate required fields + if (!name || !email || type === undefined || !txt) { + return res.status(400).json({ + error: 'Missing required fields: name, email, type, and txt are required' + }); + } + // Validate type + if (!Object.values(ContactAggregate_1.ContactType).includes(Number(type))) { + return res.status(400).json({ + error: 'Invalid contact type. Must be one of: 0 (Bug), 1 (Problem), 2 (Question), 3 (Sales), 4 (Other)' + }); + } + (0, Logger_1.logRequest)('Create contact endpoint accessed', req, res, { name, email, type, userId }); + const result = await DIContainer_1.container.createContactCommandHandler.execute({ + name, + email, + userid: userId, + type: Number(type), + txt + }); + (0, Logger_1.logRequest)('Contact created successfully', req, res, { contactId: result.id, name, email, type }); + res.status(201).json(result); + } + catch (error) { + (0, Logger_1.logError)('Create contact endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('validation')) { + return res.status(400).json({ error: 'Invalid input data', details: error.message }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +exports.default = contactRouter; +//# sourceMappingURL=contactRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/contactRouter.js.map b/SerpentRace_Backend/dist/Api/routers/contactRouter.js.map new file mode 100644 index 00000000..2aabdb45 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/contactRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"contactRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/contactRouter.ts"],"names":[],"mappings":";;AAAA,qCAAiC;AACjC,wEAAmE;AACnE,8DAAyE;AACzE,4EAAoE;AAEpE,MAAM,aAAa,GAAG,IAAA,gBAAM,GAAE,CAAC;AAE/B,gDAAgD;AAChD,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC1C,IAAI,CAAC;QACJ,0CAA0C;QAC1C,MAAM,MAAM,GAAI,GAAW,CAAC,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC;QAEjD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;QAE5C,2BAA2B;QAC3B,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC;YACnD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC3B,KAAK,EAAE,kEAAkE;aACzE,CAAC,CAAC;QACJ,CAAC;QAED,gBAAgB;QAChB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,8BAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC3B,KAAK,EAAE,gGAAgG;aACvG,CAAC,CAAC;QACJ,CAAC;QAED,IAAA,mBAAU,EAAC,kCAAkC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAExF,MAAM,MAAM,GAAG,MAAM,uBAAS,CAAC,2BAA2B,CAAC,OAAO,CAAC;YAClE,IAAI;YACJ,KAAK;YACL,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;YAClB,GAAG;SACH,CAAC,CAAC;QAEH,IAAA,mBAAU,EAAC,8BAA8B,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAA,iBAAQ,EAAC,+BAA+B,EAAE,KAAc,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAEpE,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACpE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,CAAC;QAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC1D,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.d.ts new file mode 100644 index 00000000..527ab56b --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.d.ts @@ -0,0 +1,10 @@ +declare global { + namespace Express { + interface Request { + file?: Express.Multer.File; + } + } +} +declare const router: import("express-serve-static-core").Router; +export default router; +//# sourceMappingURL=deckImportExportRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.d.ts.map new file mode 100644 index 00000000..3e9d0f6d --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"deckImportExportRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/deckImportExportRouter.ts"],"names":[],"mappings":"AAOA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC;QACd,UAAU,OAAO;YACb,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;SAC9B;KACJ;CACJ;AAED,QAAA,MAAM,MAAM,4CAAmB,CAAC;AA4GhC,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.js b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.js new file mode 100644 index 00000000..20274117 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.js @@ -0,0 +1,106 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = __importDefault(require("express")); +const multer_1 = __importDefault(require("multer")); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const AuthMiddleware_1 = require("../../Application/Services/AuthMiddleware"); +const Logger_1 = require("../../Application/Services/Logger"); +const router = express_1.default.Router(); +const container = DIContainer_1.DIContainer.getInstance(); +// Configure multer for file uploads +const upload = (0, multer_1.default)({ + storage: multer_1.default.memoryStorage(), + limits: { + fileSize: 10 * 1024 * 1024, // 10MB limit + }, + fileFilter: (req, file, cb) => { + if (file.mimetype === 'application/json' || file.originalname.endsWith('.spr')) { + cb(null, true); + } + else { + cb(new Error('Only JSON and .spr files are allowed')); + } + } +}); +// Export deck to .spr file (encrypted) - users can only export their own decks +router.get('/export/:deckId', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const { deckId } = req.params; + const userId = req.user.userId; + (0, Logger_1.logRequest)('Export deck endpoint accessed', req, res, { deckId, userId }); + // Check if user owns the deck + const deck = await container.deckRepository.findById(deckId); + if (!deck) { + (0, Logger_1.logWarning)('Deck not found for export', { deckId, userId }, req, res); + return res.status(404).json({ error: 'Deck not found' }); + } + // Users can only export their own decks + if (deck.userid !== userId) { + (0, Logger_1.logWarning)('Access denied - user attempted to export deck they do not own', { + deckId, + userId, + deckOwnerId: deck.userid + }, req, res); + return res.status(403).json({ error: 'Access denied - you can only export your own decks' }); + } + const sprData = await container.deckImportExportService.exportDeckToSpr(deckId, userId); + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', `attachment; filename="${deck.name || 'deck'}.spr"`); + (0, Logger_1.logRequest)('Deck exported successfully', req, res, { + deckId, + userId, + deckName: deck.name, + fileSize: sprData.length + }); + res.send(sprData); + } + catch (error) { + (0, Logger_1.logError)('Export deck endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Import deck from .spr file (encrypted) - imported deck will be owned by the importing user +router.post('/import', AuthMiddleware_1.authRequired, upload.single('file'), async (req, res) => { + try { + const userId = req.user.userId; + (0, Logger_1.logRequest)('Import deck endpoint accessed', req, res, { + userId, + hasFile: !!req.file, + fileName: req.file?.originalname, + fileSize: req.file?.size + }); + if (!req.file) { + (0, Logger_1.logWarning)('No file uploaded for deck import', { userId }, req, res); + return res.status(400).json({ error: 'No file uploaded' }); + } + const fileBuffer = req.file.buffer; + // Import the deck and assign ownership to the current user + const result = await container.deckImportExportService.importDeckFromSpr(fileBuffer, userId); + (0, Logger_1.logRequest)('Deck imported successfully', req, res, { + userId, + deckId: result.id, + deckName: result.name || 'Unknown', + fileName: req.file.originalname, + fileSize: req.file.size + }); + res.json({ + success: true, + message: 'Deck imported successfully and added to your collection', + deckId: result.id + }); + } + catch (error) { + (0, Logger_1.logError)('Import deck endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('Invalid')) { + return res.status(400).json({ error: 'Invalid file format or corrupted data' }); + } + else { + res.status(500).json({ error: 'Internal server error' }); + } + } +}); +exports.default = router; +//# sourceMappingURL=deckImportExportRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.js.map b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.js.map new file mode 100644 index 00000000..c72f94bc --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckImportExportRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"deckImportExportRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/deckImportExportRouter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/deckRouter.d.ts new file mode 100644 index 00000000..c5fa9fe8 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckRouter.d.ts @@ -0,0 +1,3 @@ +declare const deckRouter: import("express-serve-static-core").Router; +export default deckRouter; +//# sourceMappingURL=deckRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/deckRouter.d.ts.map new file mode 100644 index 00000000..147b9f46 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"deckRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/deckRouter.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,UAAU,4CAAW,CAAC;AAwL5B,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckRouter.js b/SerpentRace_Backend/dist/Api/routers/deckRouter.js new file mode 100644 index 00000000..690ae2dc --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckRouter.js @@ -0,0 +1,162 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = require("express"); +const AuthMiddleware_1 = require("../../Application/Services/AuthMiddleware"); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const Generalsearch_1 = require("../../Application/Search/Generalsearch"); +const Logger_1 = require("../../Application/Services/Logger"); +const deckRouter = (0, express_1.Router)(); +// Create search service that isn't in the container yet +const searchService = new Generalsearch_1.GeneralSearchService(DIContainer_1.container.userRepository, DIContainer_1.container.organizationRepository, DIContainer_1.container.deckRepository); +// Authenticated routes - Get decks with pagination (RECOMMENDED) +deckRouter.get('/page/:from/:to', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + const userOrgId = req.user.orgId; + const isAdmin = req.user.authLevel === 1; + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + (0, Logger_1.logRequest)('Get decks by page endpoint accessed', req, res, { + userId, + userOrgId, + isAdmin, + from, + to + }); + // Use paginated query handler for memory efficiency + const result = await DIContainer_1.container.getDecksByPageQueryHandler.execute({ + userId, + userOrgId, + isAdmin, + from, + to + }); + (0, Logger_1.logRequest)('Get decks page completed successfully', req, res, { + userId, + from, + to, + returnedCount: result.decks.length, + totalCount: result.totalCount + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Get decks by page endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +deckRouter.post('/', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + (0, Logger_1.logRequest)('Create deck endpoint accessed', req, res, { name: req.body.name, userId }); + const result = await DIContainer_1.container.createDeckCommandHandler.execute(req.body); + (0, Logger_1.logRequest)('Deck created successfully', req, res, { deckId: result.id, name: req.body.name, userId }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Create deck endpoint error', error, req, res); + if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique constraint'))) { + return res.status(409).json({ error: 'Deck with this name already exists' }); + } + if (error instanceof Error && error.message.includes('validation')) { + return res.status(400).json({ error: 'Invalid input data', details: error.message }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +deckRouter.get('/search', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const { q: query, limit, offset } = req.query; + (0, Logger_1.logRequest)('Search decks endpoint accessed', req, res, { query, limit, offset }); + if (!query || typeof query !== 'string') { + (0, Logger_1.logWarning)('Deck search attempted without query', { query, hasQuery: !!query }, req, res); + return res.status(400).json({ error: 'Search query is required' }); + } + const searchQuery = { + query: query.trim(), + limit: limit ? parseInt(limit) : 20, + offset: offset ? parseInt(offset) : 0 + }; + // Validate pagination parameters + if (searchQuery.limit < 1 || searchQuery.limit > 100) { + (0, Logger_1.logWarning)('Invalid deck search limit parameter', { limit: searchQuery.limit }, req, res); + return res.status(400).json({ error: 'Limit must be between 1 and 100' }); + } + if (searchQuery.offset < 0) { + (0, Logger_1.logWarning)('Invalid deck search offset parameter', { offset: searchQuery.offset }, req, res); + return res.status(400).json({ error: 'Offset must be non-negative' }); + } + const result = await searchService.searchFromUrl(req.originalUrl, searchQuery); + (0, Logger_1.logRequest)('Deck search completed successfully', req, res, { + query: searchQuery.query, + resultCount: Array.isArray(result) ? result.length : 0 + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Search decks endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +deckRouter.get('/:id', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const deckId = req.params.id; + (0, Logger_1.logRequest)('Get deck by id endpoint accessed', req, res, { deckId }); + const result = await DIContainer_1.container.getDeckByIdQueryHandler.execute({ id: deckId }); + if (!result) { + (0, Logger_1.logWarning)('Deck not found', { deckId }, req, res); + return res.status(404).json({ error: 'Deck not found' }); + } + (0, Logger_1.logRequest)('Deck retrieved successfully', req, res, { deckId }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Get deck by id endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +deckRouter.put('/:id', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const deckId = req.params.id; + const userId = req.user.userId; + (0, Logger_1.logRequest)('Update deck endpoint accessed', req, res, { deckId, userId, updateFields: Object.keys(req.body) }); + const result = await DIContainer_1.container.updateDeckCommandHandler.execute({ id: deckId, ...req.body }); + (0, Logger_1.logRequest)('Deck updated successfully', req, res, { deckId, userId }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Update deck endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Deck not found' }); + } + if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique constraint'))) { + return res.status(409).json({ error: 'Deck with this name already exists' }); + } + if (error instanceof Error && error.message.includes('validation')) { + return res.status(400).json({ error: 'Invalid input data', details: error.message }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +deckRouter.delete('/:id', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const deckId = req.params.id; + const userId = req.user.userId; + (0, Logger_1.logRequest)('Soft delete deck endpoint accessed', req, res, { deckId, userId }); + const result = await DIContainer_1.container.deleteDeckCommandHandler.execute({ id: deckId, soft: true }); + (0, Logger_1.logRequest)('Deck soft delete successful', req, res, { deckId, userId, success: result }); + res.json({ success: result }); + } + catch (error) { + (0, Logger_1.logError)('Soft delete deck endpoint error', error, req, res); + if (error instanceof Error && error.message.includes('not found')) { + return res.status(404).json({ error: 'Deck not found' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); +exports.default = deckRouter; +//# sourceMappingURL=deckRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/deckRouter.js.map b/SerpentRace_Backend/dist/Api/routers/deckRouter.js.map new file mode 100644 index 00000000..b73d333a --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/deckRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"deckRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/deckRouter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/organizationRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/organizationRouter.d.ts new file mode 100644 index 00000000..1a753f15 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/organizationRouter.d.ts @@ -0,0 +1,3 @@ +declare const organizationRouter: import("express-serve-static-core").Router; +export default organizationRouter; +//# sourceMappingURL=organizationRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/organizationRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/organizationRouter.d.ts.map new file mode 100644 index 00000000..d626760f --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/organizationRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"organizationRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/organizationRouter.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,kBAAkB,4CAAW,CAAC;AAmMpC,eAAe,kBAAkB,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/organizationRouter.js b/SerpentRace_Backend/dist/Api/routers/organizationRouter.js new file mode 100644 index 00000000..0877a92e --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/organizationRouter.js @@ -0,0 +1,179 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = require("express"); +const AuthMiddleware_1 = require("../../Application/Services/AuthMiddleware"); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const ErrorResponseService_1 = require("../../Application/Services/ErrorResponseService"); +const Generalsearch_1 = require("../../Application/Search/Generalsearch"); +const Logger_1 = require("../../Application/Services/Logger"); +const organizationRouter = (0, express_1.Router)(); +// Create search service that isn't in the container yet +const searchService = new Generalsearch_1.GeneralSearchService(DIContainer_1.container.userRepository, DIContainer_1.container.organizationRepository, DIContainer_1.container.deckRepository); +// Auth routes - Get organizations with pagination (RECOMMENDED) +organizationRouter.get('/page/:from/:to', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + (0, Logger_1.logRequest)('Get organizations by page endpoint accessed', req, res, { from, to }); + const result = await DIContainer_1.container.getOrganizationsByPageQueryHandler.execute({ from, to }); + (0, Logger_1.logRequest)('Organizations page retrieved successfully', req, res, { + from, + to, + count: result.organizations.length, + totalCount: result.totalCount + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Get organizations by page endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +organizationRouter.get('/search', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const { q: query, limit, offset } = req.query; + (0, Logger_1.logRequest)('Search organizations endpoint accessed', req, res, { query, limit, offset }); + if (!query || typeof query !== 'string') { + (0, Logger_1.logWarning)('Organization search attempted without query', { query, hasQuery: !!query }, req, res); + return res.status(400).json({ error: 'Search query is required' }); + } + const searchQuery = { + query: query.trim(), + limit: limit ? parseInt(limit) : 20, + offset: offset ? parseInt(offset) : 0 + }; + // Validate pagination parameters + if (searchQuery.limit < 1 || searchQuery.limit > 100) { + (0, Logger_1.logWarning)('Invalid organization search limit parameter', { limit: searchQuery.limit }, req, res); + return res.status(400).json({ error: 'Limit must be between 1 and 100' }); + } + if (searchQuery.offset < 0) { + (0, Logger_1.logWarning)('Invalid organization search offset parameter', { offset: searchQuery.offset }, req, res); + return res.status(400).json({ error: 'Offset must be non-negative' }); + } + const result = await searchService.searchFromUrl(req.originalUrl, searchQuery); + (0, Logger_1.logRequest)('Organization search completed successfully', req, res, { + query: searchQuery.query, + resultCount: Array.isArray(result) ? result.length : 0 + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Search organizations endpoint error', error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +// Get organization login URL +organizationRouter.get('/:orgId/login-url', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + const { orgId } = req.params; + (0, Logger_1.logRequest)('Get organization login URL endpoint accessed', req, res, { + userId, + organizationId: orgId + }); + const result = await DIContainer_1.container.getOrganizationLoginUrlQueryHandler.execute({ + organizationId: orgId + }); + if (!result) { + (0, Logger_1.logWarning)('Organization login URL not found', { + organizationId: orgId, + userId + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'Organization login URL not found'); + } + (0, Logger_1.logRequest)('Organization login URL retrieved successfully', req, res, { + organizationId: orgId, + organizationName: result.organizationName, + hasUrl: !!result.loginUrl, + userId + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Get organization login URL endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Process third-party authentication callback +organizationRouter.post('/auth-callback', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + const { organizationId, status, authToken } = req.body; + (0, Logger_1.logRequest)('Organization auth callback endpoint accessed', req, res, { + userId, + organizationId, + status, + hasAuthToken: !!authToken + }); + // Validate required fields + if (!organizationId || !status) { + (0, Logger_1.logWarning)('Missing required fields for organization auth callback', { + organizationId: !!organizationId, + status: !!status, + userId + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'organizationId and status are required'); + } + if (status !== 'ok' && status !== 'not_ok') { + (0, Logger_1.logWarning)('Invalid status value for organization auth callback', { + status, + userId, + organizationId + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'status must be either "ok" or "not_ok"'); + } + const result = await DIContainer_1.container.processOrgAuthCallbackCommandHandler.execute({ + organizationId, + userId, + status, + authToken + }); + if (!result.success) { + if (result.message.includes('not found')) { + (0, Logger_1.logWarning)('Organization auth callback failed - entity not found', { + userId, + organizationId, + message: result.message + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, result.message); + } + if (result.message.includes('does not belong')) { + (0, Logger_1.logWarning)('Organization auth callback failed - authorization error', { + userId, + organizationId, + message: result.message + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendForbidden(res, result.message); + } + if (result.message.includes('authentication failed')) { + (0, Logger_1.logAuth)('Organization authentication failed via callback', userId, { + organizationId, + status + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendUnauthorized(res, result.message); + } + (0, Logger_1.logError)('Organization auth callback internal error', new Error(result.message), req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } + (0, Logger_1.logAuth)('Organization auth callback processed successfully', userId, { + organizationId, + status, + updatedFields: result.updatedFields + }, req, res); + res.json({ + success: result.success, + message: result.message, + updatedFields: result.updatedFields + }); + } + catch (error) { + (0, Logger_1.logError)('Organization auth callback endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +exports.default = organizationRouter; +//# sourceMappingURL=organizationRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/organizationRouter.js.map b/SerpentRace_Backend/dist/Api/routers/organizationRouter.js.map new file mode 100644 index 00000000..e6106000 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/organizationRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"organizationRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/organizationRouter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/userRouter.d.ts b/SerpentRace_Backend/dist/Api/routers/userRouter.d.ts new file mode 100644 index 00000000..23df20cc --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/userRouter.d.ts @@ -0,0 +1,3 @@ +declare const userRouter: import("express-serve-static-core").Router; +export default userRouter; +//# sourceMappingURL=userRouter.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/userRouter.d.ts.map b/SerpentRace_Backend/dist/Api/routers/userRouter.d.ts.map new file mode 100644 index 00000000..0cb1b195 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/userRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userRouter.d.ts","sourceRoot":"","sources":["../../../src/Api/routers/userRouter.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,UAAU,4CAAW,CAAC;AA+J5B,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/userRouter.js b/SerpentRace_Backend/dist/Api/routers/userRouter.js new file mode 100644 index 00000000..f0f27126 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/userRouter.js @@ -0,0 +1,139 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = require("express"); +const AuthMiddleware_1 = require("../../Application/Services/AuthMiddleware"); +const DIContainer_1 = require("../../Application/Services/DIContainer"); +const ErrorResponseService_1 = require("../../Application/Services/ErrorResponseService"); +const ValidationMiddleware_1 = require("../../Application/Services/ValidationMiddleware"); +const Generalsearch_1 = require("../../Application/Search/Generalsearch"); +const Logger_1 = require("../../Application/Services/Logger"); +const userRouter = (0, express_1.Router)(); +// Create search service that isn't in the container yet +const searchService = new Generalsearch_1.GeneralSearchService(DIContainer_1.container.userRepository, DIContainer_1.container.organizationRepository, DIContainer_1.container.deckRepository); +// Login endpoint +userRouter.post('/login', ValidationMiddleware_1.ValidationMiddleware.combine([ + ValidationMiddleware_1.ValidationMiddleware.validateRequiredFields(['username', 'password']), + ValidationMiddleware_1.ValidationMiddleware.validateStringLength({ + username: { min: 3, max: 50 }, + password: { min: 6, max: 100 } + }) +]), async (req, res) => { + try { + (0, Logger_1.logRequest)('Login endpoint accessed', req, res, { username: req.body.username }); + const { username, password } = req.body; + const result = await DIContainer_1.container.loginCommandHandler.execute({ username, password }); + if (result) { + (0, Logger_1.logAuth)('User login successful', result.user.id, { username: result.user.username }, req, res); + res.json(result); + } + else { + return ErrorResponseService_1.ErrorResponseService.sendUnauthorized(res, 'Invalid username or password'); + } + } + catch (error) { + (0, Logger_1.logError)('Login endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('Invalid username')) { + return ErrorResponseService_1.ErrorResponseService.sendUnauthorized(res, 'Invalid username or password'); + } + if (error.message.includes('Invalid password')) { + return ErrorResponseService_1.ErrorResponseService.sendUnauthorized(res, 'Invalid username or password'); + } + if (error.message.includes('not verified')) { + return ErrorResponseService_1.ErrorResponseService.sendUnauthorized(res, 'Please verify your email address'); + } + if (error.message.includes('deactivated')) { + return ErrorResponseService_1.ErrorResponseService.sendUnauthorized(res, 'Account has been deactivated'); + } + } + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Create user endpoint +userRouter.post('/create', ValidationMiddleware_1.ValidationMiddleware.combine([ + ValidationMiddleware_1.ValidationMiddleware.validateRequiredFields(['username', 'email', 'password']), + ValidationMiddleware_1.ValidationMiddleware.validateEmailFormat(['email']), + ValidationMiddleware_1.ValidationMiddleware.validateStringLength({ + username: { min: 3, max: 50 }, + password: { min: 6, max: 100 } + }) +]), async (req, res) => { + try { + (0, Logger_1.logRequest)('Create user endpoint accessed', req, res, { + username: req.body.username, + email: req.body.email + }); + const result = await DIContainer_1.container.createUserCommandHandler.execute(req.body); + (0, Logger_1.logRequest)('User created successfully', req, res, { + userId: result.id, + username: result.username + }); + res.status(201).json(result); + } + catch (error) { + (0, Logger_1.logError)('Create user endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('already exists')) { + return ErrorResponseService_1.ErrorResponseService.sendConflict(res, error.message); + } + if (error.message.includes('validation')) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, error.message); + } + } + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Get user profile (current user) +userRouter.get('/profile', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + (0, Logger_1.logRequest)('Get user profile endpoint accessed', req, res, { userId }); + const result = await DIContainer_1.container.getUserByIdQueryHandler.execute({ id: userId }); + if (!result) { + (0, Logger_1.logWarning)('User profile not found', { userId }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'User not found'); + } + (0, Logger_1.logRequest)('User profile retrieved successfully', req, res, { + userId, + username: result.username + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Get user profile endpoint error', error, req, res); + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +// Update user profile (current user) +userRouter.patch('/profile', AuthMiddleware_1.authRequired, async (req, res) => { + try { + const userId = req.user.userId; + (0, Logger_1.logRequest)('Update user profile endpoint accessed', req, res, { + userId, + fieldsToUpdate: Object.keys(req.body) + }); + const result = await DIContainer_1.container.updateUserCommandHandler.execute({ id: userId, ...req.body }); + if (!result) { + return ErrorResponseService_1.ErrorResponseService.sendNotFound(res, 'User not found'); + } + (0, Logger_1.logRequest)('User profile updated successfully', req, res, { + userId, + username: result.username + }); + res.json(result); + } + catch (error) { + (0, Logger_1.logError)('Update user profile endpoint error', error, req, res); + if (error instanceof Error) { + if (error.message.includes('already exists')) { + return ErrorResponseService_1.ErrorResponseService.sendConflict(res, error.message); + } + if (error.message.includes('validation')) { + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, error.message); + } + } + return ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } +}); +exports.default = userRouter; +//# sourceMappingURL=userRouter.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/routers/userRouter.js.map b/SerpentRace_Backend/dist/Api/routers/userRouter.js.map new file mode 100644 index 00000000..ecb8d61b --- /dev/null +++ b/SerpentRace_Backend/dist/Api/routers/userRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../../src/Api/routers/userRouter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.d.ts b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.d.ts new file mode 100644 index 00000000..8e49e1f4 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.d.ts @@ -0,0 +1,42 @@ +export declare const swaggerOptions: { + definition: { + openapi: string; + info: { + title: string; + version: string; + description: string; + contact: { + name: string; + email: string; + }; + license: { + name: string; + url: string; + }; + }; + servers: { + url: string; + description: string; + }[]; + components: { + securitySchemes: { + bearerAuth: { + type: string; + scheme: string; + bearerFormat: string; + description: string; + }; + }; + }; + security: { + bearerAuth: never[]; + }[]; + tags: { + name: string; + description: string; + }[]; + }; + apis: string[]; +}; +export declare const swaggerSpec: object; +//# sourceMappingURL=swaggerConfig.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.d.ts.map b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.d.ts.map new file mode 100644 index 00000000..0b4705ac --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"swaggerConfig.d.ts","sourceRoot":"","sources":["../../../src/Api/swagger/swaggerConfig.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmE1B,CAAC;AAEF,eAAO,MAAM,WAAW,QAA+B,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.js b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.js new file mode 100644 index 00000000..d7f11ee7 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.js @@ -0,0 +1,77 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.swaggerSpec = exports.swaggerOptions = void 0; +const swagger_jsdoc_1 = __importDefault(require("swagger-jsdoc")); +exports.swaggerOptions = { + definition: { + openapi: '3.0.0', + info: { + title: 'SerpentRace API', + version: '1.0.0', + description: 'Comprehensive API documentation for SerpentRace Backend', + contact: { + name: 'SerpentRace Development Team', + email: 'dev@serpentrace.com' + }, + license: { + name: 'MIT', + url: 'https://opensource.org/licenses/MIT' + } + }, + servers: [ + { + url: 'http://localhost:3000', + description: 'Local development server' + }, + { + url: 'https://api.serpentrace.com', + description: 'Production server' + } + ], + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + description: 'Enter JWT token obtained from /api/users/login' + } + } + }, + security: [{ bearerAuth: [] }], + tags: [ + { + name: 'Users', + description: 'User authentication and profile management' + }, + { + name: 'Organizations', + description: 'Organization management and authentication' + }, + { + name: 'Decks', + description: 'Deck creation, management, and gameplay' + }, + { + name: 'Chats', + description: 'Real-time chat and messaging system' + }, + { + name: 'Contacts', + description: 'Contact form and support requests' + }, + { + name: 'Deck Import/Export', + description: 'Import and export deck functionality' + } + ] + }, + apis: [ + './src/Api/swagger/swaggerDefinitions.ts' + ], +}; +exports.swaggerSpec = (0, swagger_jsdoc_1.default)(exports.swaggerOptions); +//# sourceMappingURL=swaggerConfig.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.js.map b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.js.map new file mode 100644 index 00000000..4a5a5ff5 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"swaggerConfig.js","sourceRoot":"","sources":["../../../src/Api/swagger/swaggerConfig.ts"],"names":[],"mappings":";;;;;;AAAA,kEAAyC;AAE5B,QAAA,cAAc,GAAG;IAC5B,UAAU,EAAE;QACV,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,yDAAyD;YACtE,OAAO,EAAE;gBACP,IAAI,EAAE,8BAA8B;gBACpC,KAAK,EAAE,qBAAqB;aAC7B;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,KAAK;gBACX,GAAG,EAAE,qCAAqC;aAC3C;SACF;QACD,OAAO,EAAE;YACP;gBACE,GAAG,EAAE,uBAAuB;gBAC5B,WAAW,EAAE,0BAA0B;aACxC;YACD;gBACE,GAAG,EAAE,6BAA6B;gBAClC,WAAW,EAAE,mBAAmB;aACjC;SACF;QACD,UAAU,EAAE;YACV,eAAe,EAAE;gBACf,UAAU,EAAE;oBACV,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,QAAQ;oBAChB,YAAY,EAAE,KAAK;oBACnB,WAAW,EAAE,gDAAgD;iBAC9D;aACF;SACF;QACD,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QAC9B,IAAI,EAAE;YACJ;gBACE,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,4CAA4C;aAC1D;YACD;gBACE,IAAI,EAAE,eAAe;gBACrB,WAAW,EAAE,4CAA4C;aAC1D;YACD;gBACE,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,yCAAyC;aACvD;YACD;gBACE,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,qCAAqC;aACnD;YACD;gBACE,IAAI,EAAE,UAAU;gBAChB,WAAW,EAAE,mCAAmC;aACjD;YACD;gBACE,IAAI,EAAE,oBAAoB;gBAC1B,WAAW,EAAE,sCAAsC;aACpD;SACF;KACF;IACD,IAAI,EAAE;QACJ,yCAAyC;KAC1C;CACF,CAAC;AAEW,QAAA,WAAW,GAAG,IAAA,uBAAY,EAAC,sBAAc,CAAC,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.d.ts b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.d.ts new file mode 100644 index 00000000..64290d9d --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.d.ts @@ -0,0 +1,1377 @@ +/** + * @swagger + * components: + * securitySchemes: + * bearerAuth: + * type: http + * scheme: bearer + * bearerFormat: JWT + * schemas: + * User: + * type: object + * properties: + * id: + * type: string + * format: uuid + * username: + * type: string + * email: + * type: string + * format: email + * fname: + * type: string + * lname: + * type: string + * phone: + * type: string + * nullable: true + * type: + * type: string + * state: + * type: integer + * regdate: + * type: string + * format: date-time + * updatedate: + * type: string + * format: date-time + * orgid: + * type: string + * nullable: true + * + * CreateUserRequest: + * type: object + * required: + * - username + * - email + * - password + * - fname + * - lname + * - type + * properties: + * username: + * type: string + * email: + * type: string + * format: email + * password: + * type: string + * format: password + * fname: + * type: string + * lname: + * type: string + * phone: + * type: string + * type: + * type: string + * + * LoginRequest: + * type: object + * required: + * - username + * - password + * properties: + * username: + * type: string + * password: + * type: string + * format: password + * + * LoginResponse: + * type: object + * properties: + * token: + * type: string + * user: + * $ref: '#/components/schemas/User' + * requiresOrgReauth: + * type: boolean + * orgLoginUrl: + * type: string + * organizationName: + * type: string + * + * UpdateProfileRequest: + * type: object + * properties: + * fname: + * type: string + * lname: + * type: string + * phone: + * type: string + * email: + * type: string + * format: email + * + * Organization: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * contactfname: + * type: string + * contactlname: + * type: string + * contactphone: + * type: string + * contactemail: + * type: string + * format: email + * state: + * type: integer + * regdate: + * type: string + * format: date-time + * updatedate: + * type: string + * format: date-time + * url: + * type: string + * nullable: true + * userinorg: + * type: integer + * maxOrganizationalDecks: + * type: integer + * + * Deck: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * type: + * type: integer + * enum: [0, 1, 2, 3] + * description: 0=JOKER, 1=LUCK, 2=QUESTION, 3=GENERAL + * userid: + * type: string + * format: uuid + * creationdate: + * type: string + * format: date-time + * cards: + * type: array + * items: + * type: object + * playedNumber: + * type: integer + * ctype: + * type: integer + * enum: [0, 1, 2] + * description: 0=PUBLIC, 1=ORGANIZATIONAL, 2=PRIVATE + * updatedate: + * type: string + * format: date-time + * state: + * type: integer + * enum: [0, 1, 2] + * description: 0=ACTIVE, 1=INACTIVE, 2=SOFT_DELETE + * organization: + * $ref: '#/components/schemas/Organization' + * nullable: true + * + * CreateDeckRequest: + * type: object + * required: + * - name + * - type + * - cards + * properties: + * name: + * type: string + * type: + * type: integer + * cards: + * type: array + * items: + * type: object + * ctype: + * type: integer + * + * Contact: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * email: + * type: string + * format: email + * userid: + * type: string + * format: uuid + * nullable: true + * type: + * type: integer + * enum: [0, 1, 2] + * description: 0=QUESTION, 1=BUG_REPORT, 2=SUGGESTION + * txt: + * type: string + * state: + * type: integer + * createDate: + * type: string + * format: date-time + * updateDate: + * type: string + * format: date-time + * adminResponse: + * type: string + * nullable: true + * responseDate: + * type: string + * format: date-time + * nullable: true + * respondedBy: + * type: string + * nullable: true + * + * CreateContactRequest: + * type: object + * required: + * - name + * - email + * - type + * - txt + * properties: + * name: + * type: string + * email: + * type: string + * format: email + * type: + * type: integer + * txt: + * type: string + * + * Chat: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * type: + * type: integer + * participants: + * type: array + * items: + * type: string + * creatorId: + * type: string + * gameId: + * type: string + * nullable: true + * createDate: + * type: string + * format: date-time + * updateDate: + * type: string + * format: date-time + * state: + * type: integer + * + * ChatMessage: + * type: object + * properties: + * id: + * type: string + * format: uuid + * senderId: + * type: string + * senderName: + * type: string + * message: + * type: string + * timestamp: + * type: string + * format: date-time + * chatId: + * type: string + * + * Error: + * type: object + * properties: + * error: + * type: string + * timestamp: + * type: string + * format: date-time + * details: + * type: string + * + * paths: + * /api/users/login: + * post: + * tags: [Users] + * summary: User login + * description: Authenticate user and return JWT token + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginRequest' + * responses: + * 200: + * description: Login successful + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginResponse' + * 401: + * description: Invalid credentials + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * + * /api/users/create: + * post: + * tags: [Users] + * summary: Create new user + * description: Register a new user account + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateUserRequest' + * responses: + * 201: + * description: User created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 400: + * description: Validation error + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * + * /api/users/profile: + * get: + * tags: [Users] + * summary: Get user profile + * description: Get current user's profile information + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: User profile data + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 401: + * description: Unauthorized + * patch: + * tags: [Users] + * summary: Update user profile + * description: Update current user's profile information + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/UpdateProfileRequest' + * responses: + * 200: + * description: Profile updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 400: + * description: Validation error + * 401: + * description: Unauthorized + * + * /api/organizations/page/{from}/{to}: + * get: + * tags: [Organizations] + * summary: Get organizations by page + * description: Retrieve paginated list of organizations + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated organizations + * content: + * application/json: + * schema: + * type: object + * properties: + * organizations: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * totalCount: + * type: integer + * + * /api/organizations/search: + * get: + * tags: [Organizations] + * summary: Search organizations + * description: Search organizations by term + * security: + * - bearerAuth: [] + * parameters: + * - name: term + * in: query + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: object + * properties: + * results: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * totalCount: + * type: integer + * + * /api/organizations/{orgId}/login-url: + * get: + * tags: [Organizations] + * summary: Get organization login URL + * description: Get OAuth login URL for organization + * security: + * - bearerAuth: [] + * parameters: + * - name: orgId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Login URL + * content: + * application/json: + * schema: + * type: object + * properties: + * loginUrl: + * type: string + * + * /api/organizations/auth-callback: + * post: + * tags: [Organizations] + * summary: OAuth callback + * description: Handle OAuth callback from organization + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * code: + * type: string + * state: + * type: string + * responses: + * 200: + * description: Authentication successful + * 400: + * description: Invalid callback data + * + * /api/decks/page/{from}/{to}: + * get: + * tags: [Decks] + * summary: Get decks by page + * description: Retrieve paginated list of decks + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated decks + * content: + * application/json: + * schema: + * type: object + * properties: + * decks: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * totalCount: + * type: integer + * + * /api/decks: + * post: + * tags: [Decks] + * summary: Create deck + * description: Create a new deck + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateDeckRequest' + * responses: + * 201: + * description: Deck created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * + * /api/decks/search: + * get: + * tags: [Decks] + * summary: Search decks + * description: Search decks by term + * security: + * - bearerAuth: [] + * parameters: + * - name: term + * in: query + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: object + * properties: + * results: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * totalCount: + * type: integer + * + * /api/decks/{id}: + * get: + * tags: [Decks] + * summary: Get deck by ID + * description: Retrieve a specific deck by ID + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Deck details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * 404: + * description: Deck not found + * put: + * tags: [Decks] + * summary: Update deck + * description: Update an existing deck + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateDeckRequest' + * responses: + * 200: + * description: Deck updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * delete: + * tags: [Decks] + * summary: Delete deck + * description: Delete a deck (soft delete) + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Deck deleted successfully + * 404: + * description: Deck not found + * + * /api/chats/user-chats: + * get: + * tags: [Chats] + * summary: Get user chats + * description: Get all chats for the current user + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: User chats + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Chat' + * + * /api/chats/history/{chatId}: + * get: + * tags: [Chats] + * summary: Get chat history + * description: Get message history for a chat + * security: + * - bearerAuth: [] + * parameters: + * - name: chatId + * in: path + * required: true + * schema: + * type: string + * - name: page + * in: query + * schema: + * type: integer + * - name: limit + * in: query + * schema: + * type: integer + * responses: + * 200: + * description: Chat history + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/ChatMessage' + * + * /api/chats/create: + * post: + * tags: [Chats] + * summary: Create chat + * description: Create a new chat room + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * - gameId + * properties: + * name: + * type: string + * gameId: + * type: string + * password: + * type: string + * nullable: true + * responses: + * 201: + * description: Chat created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Chat' + * + * /api/chats/message: + * post: + * tags: [Chats] + * summary: Send message + * description: Send a message to a chat + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - chatId + * - message + * properties: + * chatId: + * type: string + * message: + * type: string + * responses: + * 201: + * description: Message sent successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ChatMessage' + * + * /api/chats/archive/{chatId}: + * post: + * tags: [Chats] + * summary: Archive chat + * description: Archive a chat room + * security: + * - bearerAuth: [] + * parameters: + * - name: chatId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Chat archived successfully + * 404: + * description: Chat not found + * + * /api/chats/restore/{chatId}: + * post: + * tags: [Chats] + * summary: Restore chat + * description: Restore an archived chat room + * security: + * - bearerAuth: [] + * parameters: + * - name: chatId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Chat restored successfully + * 404: + * description: Chat not found + * + * /api/chats/archived/game/{gameId}: + * get: + * tags: [Chats] + * summary: Get archived chats by game + * description: Get all archived chats for a specific game + * security: + * - bearerAuth: [] + * parameters: + * - name: gameId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Archived chats + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Chat' + * + * /api/deck-import-export/export/{deckId}: + * get: + * tags: [Import/Export] + * summary: Export deck + * description: Export a deck as JSON or .spr file + * security: + * - bearerAuth: [] + * parameters: + * - name: deckId + * in: path + * required: true + * schema: + * type: string + * - name: format + * in: query + * schema: + * type: string + * enum: [json, spr] + * default: json + * responses: + * 200: + * description: Deck exported successfully + * content: + * application/json: + * schema: + * type: object + * application/octet-stream: + * schema: + * type: string + * format: binary + * + * /api/deck-import-export/import: + * post: + * tags: [Import/Export] + * summary: Import deck + * description: Import a deck from JSON or .spr file + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * properties: + * file: + * type: string + * format: binary + * responses: + * 201: + * description: Deck imported successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * + * /api/admin/users/page/{from}/{to}: + * get: + * tags: [Admin - Users] + * summary: Get users by page (Admin) + * description: Admin endpoint to retrieve paginated list of users + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated users + * content: + * application/json: + * schema: + * type: object + * properties: + * users: + * type: array + * items: + * $ref: '#/components/schemas/User' + * totalCount: + * type: integer + * + * /api/admin/users/{userId}: + * get: + * tags: [Admin - Users] + * summary: Get user by ID (Admin) + * description: Admin endpoint to get specific user details + * security: + * - bearerAuth: [] + * parameters: + * - name: userId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: User details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * delete: + * tags: [Admin - Users] + * summary: Delete user (Admin) + * description: Admin endpoint to delete a user + * security: + * - bearerAuth: [] + * parameters: + * - name: userId + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: User deleted successfully + * + * /api/admin/users/search/{searchTerm}: + * get: + * tags: [Admin - Users] + * summary: Search users (Admin) + * description: Admin endpoint to search users + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/User' + * + * /api/admin/users/{userId}/deactivate: + * post: + * tags: [Admin - Users] + * summary: Deactivate user (Admin) + * description: Admin endpoint to deactivate a user + * security: + * - bearerAuth: [] + * parameters: + * - name: userId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: User deactivated successfully + * + * /api/admin/decks/page/{from}/{to}: + * get: + * tags: [Admin - Decks] + * summary: Get decks by page (Admin) + * description: Admin endpoint to retrieve paginated list of decks + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated decks + * content: + * application/json: + * schema: + * type: object + * properties: + * decks: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * totalCount: + * type: integer + * + * /api/admin/decks/{id}: + * get: + * tags: [Admin - Decks] + * summary: Get deck by ID (Admin) + * description: Admin endpoint to get specific deck details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Deck details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * + * /api/admin/decks/search/{searchTerm}: + * get: + * tags: [Admin - Decks] + * summary: Search decks (Admin) + * description: Admin endpoint to search decks + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * + * /api/admin/decks/{id}/hard: + * delete: + * tags: [Admin - Decks] + * summary: Hard delete deck (Admin) + * description: Admin endpoint to permanently delete a deck + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Deck permanently deleted + * + * /api/admin/organizations: + * post: + * tags: [Admin - Organizations] + * summary: Create organization (Admin) + * description: Admin endpoint to create a new organization + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * properties: + * name: + * type: string + * description: + * type: string + * responses: + * 201: + * description: Organization created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Organization' + * + * /api/admin/organizations/page/{from}/{to}: + * get: + * tags: [Admin - Organizations] + * summary: Get organizations by page (Admin) + * description: Admin endpoint to retrieve paginated list of organizations + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated organizations + * content: + * application/json: + * schema: + * type: object + * properties: + * organizations: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * totalCount: + * type: integer + * + * /api/admin/organizations/{id}: + * get: + * tags: [Admin - Organizations] + * summary: Get organization by ID (Admin) + * description: Admin endpoint to get specific organization details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Organization details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Organization' + * delete: + * tags: [Admin - Organizations] + * summary: Delete organization (Admin) + * description: Admin endpoint to soft delete an organization + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Organization deleted successfully + * + * /api/admin/organizations/search/{searchTerm}: + * get: + * tags: [Admin - Organizations] + * summary: Search organizations (Admin) + * description: Admin endpoint to search organizations + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * + * /api/admin/organizations/{id}/hard: + * delete: + * tags: [Admin - Organizations] + * summary: Hard delete organization (Admin) + * description: Admin endpoint to permanently delete an organization + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Organization permanently deleted + * + * /api/admin/chats/page/{from}/{to}: + * get: + * tags: [Admin - Chats] + * summary: Get chats by page (Admin) + * description: Admin endpoint to retrieve paginated list of chats + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated chats + * content: + * application/json: + * schema: + * type: object + * properties: + * chats: + * type: array + * items: + * $ref: '#/components/schemas/Chat' + * totalCount: + * type: integer + * + * /api/admin/chats/{id}: + * get: + * tags: [Admin - Chats] + * summary: Get chat by ID (Admin) + * description: Admin endpoint to get specific chat details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Chat details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Chat' + * + * /api/admin/contacts/page/{from}/{to}: + * get: + * tags: [Admin - Contacts] + * summary: Get contacts by page (Admin) + * description: Admin endpoint to retrieve paginated list of contacts + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated contacts + * content: + * application/json: + * schema: + * type: object + * properties: + * contacts: + * type: array + * items: + * $ref: '#/components/schemas/Contact' + * totalCount: + * type: integer + * + * /api/admin/contacts/{id}: + * get: + * tags: [Admin - Contacts] + * summary: Get contact by ID (Admin) + * description: Admin endpoint to get specific contact details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Contact details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Contact' + * + * /api/admin/contacts/search/{searchTerm}: + * get: + * tags: [Admin - Contacts] + * summary: Search contacts (Admin) + * description: Admin endpoint to search contacts + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Contact' + * + * /api/contacts: + * post: + * tags: [Contacts] + * summary: Create contact + * description: Create a new contact message + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateContactRequest' + * responses: + * 201: + * description: Contact created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Contact' + */ +export {}; +//# sourceMappingURL=swaggerDefinitions.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.d.ts.map b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.d.ts.map new file mode 100644 index 00000000..9f932502 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"swaggerDefinitions.d.ts","sourceRoot":"","sources":["../../../src/Api/swagger/swaggerDefinitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA81CG;AAEH,OAAO,EAAE,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.js b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.js new file mode 100644 index 00000000..e9b1be5d --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.js @@ -0,0 +1,1378 @@ +"use strict"; +/** + * @swagger + * components: + * securitySchemes: + * bearerAuth: + * type: http + * scheme: bearer + * bearerFormat: JWT + * schemas: + * User: + * type: object + * properties: + * id: + * type: string + * format: uuid + * username: + * type: string + * email: + * type: string + * format: email + * fname: + * type: string + * lname: + * type: string + * phone: + * type: string + * nullable: true + * type: + * type: string + * state: + * type: integer + * regdate: + * type: string + * format: date-time + * updatedate: + * type: string + * format: date-time + * orgid: + * type: string + * nullable: true + * + * CreateUserRequest: + * type: object + * required: + * - username + * - email + * - password + * - fname + * - lname + * - type + * properties: + * username: + * type: string + * email: + * type: string + * format: email + * password: + * type: string + * format: password + * fname: + * type: string + * lname: + * type: string + * phone: + * type: string + * type: + * type: string + * + * LoginRequest: + * type: object + * required: + * - username + * - password + * properties: + * username: + * type: string + * password: + * type: string + * format: password + * + * LoginResponse: + * type: object + * properties: + * token: + * type: string + * user: + * $ref: '#/components/schemas/User' + * requiresOrgReauth: + * type: boolean + * orgLoginUrl: + * type: string + * organizationName: + * type: string + * + * UpdateProfileRequest: + * type: object + * properties: + * fname: + * type: string + * lname: + * type: string + * phone: + * type: string + * email: + * type: string + * format: email + * + * Organization: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * contactfname: + * type: string + * contactlname: + * type: string + * contactphone: + * type: string + * contactemail: + * type: string + * format: email + * state: + * type: integer + * regdate: + * type: string + * format: date-time + * updatedate: + * type: string + * format: date-time + * url: + * type: string + * nullable: true + * userinorg: + * type: integer + * maxOrganizationalDecks: + * type: integer + * + * Deck: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * type: + * type: integer + * enum: [0, 1, 2, 3] + * description: 0=JOKER, 1=LUCK, 2=QUESTION, 3=GENERAL + * userid: + * type: string + * format: uuid + * creationdate: + * type: string + * format: date-time + * cards: + * type: array + * items: + * type: object + * playedNumber: + * type: integer + * ctype: + * type: integer + * enum: [0, 1, 2] + * description: 0=PUBLIC, 1=ORGANIZATIONAL, 2=PRIVATE + * updatedate: + * type: string + * format: date-time + * state: + * type: integer + * enum: [0, 1, 2] + * description: 0=ACTIVE, 1=INACTIVE, 2=SOFT_DELETE + * organization: + * $ref: '#/components/schemas/Organization' + * nullable: true + * + * CreateDeckRequest: + * type: object + * required: + * - name + * - type + * - cards + * properties: + * name: + * type: string + * type: + * type: integer + * cards: + * type: array + * items: + * type: object + * ctype: + * type: integer + * + * Contact: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * email: + * type: string + * format: email + * userid: + * type: string + * format: uuid + * nullable: true + * type: + * type: integer + * enum: [0, 1, 2] + * description: 0=QUESTION, 1=BUG_REPORT, 2=SUGGESTION + * txt: + * type: string + * state: + * type: integer + * createDate: + * type: string + * format: date-time + * updateDate: + * type: string + * format: date-time + * adminResponse: + * type: string + * nullable: true + * responseDate: + * type: string + * format: date-time + * nullable: true + * respondedBy: + * type: string + * nullable: true + * + * CreateContactRequest: + * type: object + * required: + * - name + * - email + * - type + * - txt + * properties: + * name: + * type: string + * email: + * type: string + * format: email + * type: + * type: integer + * txt: + * type: string + * + * Chat: + * type: object + * properties: + * id: + * type: string + * format: uuid + * name: + * type: string + * type: + * type: integer + * participants: + * type: array + * items: + * type: string + * creatorId: + * type: string + * gameId: + * type: string + * nullable: true + * createDate: + * type: string + * format: date-time + * updateDate: + * type: string + * format: date-time + * state: + * type: integer + * + * ChatMessage: + * type: object + * properties: + * id: + * type: string + * format: uuid + * senderId: + * type: string + * senderName: + * type: string + * message: + * type: string + * timestamp: + * type: string + * format: date-time + * chatId: + * type: string + * + * Error: + * type: object + * properties: + * error: + * type: string + * timestamp: + * type: string + * format: date-time + * details: + * type: string + * + * paths: + * /api/users/login: + * post: + * tags: [Users] + * summary: User login + * description: Authenticate user and return JWT token + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginRequest' + * responses: + * 200: + * description: Login successful + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginResponse' + * 401: + * description: Invalid credentials + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * + * /api/users/create: + * post: + * tags: [Users] + * summary: Create new user + * description: Register a new user account + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateUserRequest' + * responses: + * 201: + * description: User created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 400: + * description: Validation error + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' + * + * /api/users/profile: + * get: + * tags: [Users] + * summary: Get user profile + * description: Get current user's profile information + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: User profile data + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 401: + * description: Unauthorized + * patch: + * tags: [Users] + * summary: Update user profile + * description: Update current user's profile information + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/UpdateProfileRequest' + * responses: + * 200: + * description: Profile updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * 400: + * description: Validation error + * 401: + * description: Unauthorized + * + * /api/organizations/page/{from}/{to}: + * get: + * tags: [Organizations] + * summary: Get organizations by page + * description: Retrieve paginated list of organizations + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated organizations + * content: + * application/json: + * schema: + * type: object + * properties: + * organizations: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * totalCount: + * type: integer + * + * /api/organizations/search: + * get: + * tags: [Organizations] + * summary: Search organizations + * description: Search organizations by term + * security: + * - bearerAuth: [] + * parameters: + * - name: term + * in: query + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: object + * properties: + * results: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * totalCount: + * type: integer + * + * /api/organizations/{orgId}/login-url: + * get: + * tags: [Organizations] + * summary: Get organization login URL + * description: Get OAuth login URL for organization + * security: + * - bearerAuth: [] + * parameters: + * - name: orgId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Login URL + * content: + * application/json: + * schema: + * type: object + * properties: + * loginUrl: + * type: string + * + * /api/organizations/auth-callback: + * post: + * tags: [Organizations] + * summary: OAuth callback + * description: Handle OAuth callback from organization + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * code: + * type: string + * state: + * type: string + * responses: + * 200: + * description: Authentication successful + * 400: + * description: Invalid callback data + * + * /api/decks/page/{from}/{to}: + * get: + * tags: [Decks] + * summary: Get decks by page + * description: Retrieve paginated list of decks + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated decks + * content: + * application/json: + * schema: + * type: object + * properties: + * decks: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * totalCount: + * type: integer + * + * /api/decks: + * post: + * tags: [Decks] + * summary: Create deck + * description: Create a new deck + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateDeckRequest' + * responses: + * 201: + * description: Deck created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * + * /api/decks/search: + * get: + * tags: [Decks] + * summary: Search decks + * description: Search decks by term + * security: + * - bearerAuth: [] + * parameters: + * - name: term + * in: query + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: object + * properties: + * results: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * totalCount: + * type: integer + * + * /api/decks/{id}: + * get: + * tags: [Decks] + * summary: Get deck by ID + * description: Retrieve a specific deck by ID + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Deck details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * 404: + * description: Deck not found + * put: + * tags: [Decks] + * summary: Update deck + * description: Update an existing deck + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateDeckRequest' + * responses: + * 200: + * description: Deck updated successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * delete: + * tags: [Decks] + * summary: Delete deck + * description: Delete a deck (soft delete) + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Deck deleted successfully + * 404: + * description: Deck not found + * + * /api/chats/user-chats: + * get: + * tags: [Chats] + * summary: Get user chats + * description: Get all chats for the current user + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: User chats + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Chat' + * + * /api/chats/history/{chatId}: + * get: + * tags: [Chats] + * summary: Get chat history + * description: Get message history for a chat + * security: + * - bearerAuth: [] + * parameters: + * - name: chatId + * in: path + * required: true + * schema: + * type: string + * - name: page + * in: query + * schema: + * type: integer + * - name: limit + * in: query + * schema: + * type: integer + * responses: + * 200: + * description: Chat history + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/ChatMessage' + * + * /api/chats/create: + * post: + * tags: [Chats] + * summary: Create chat + * description: Create a new chat room + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * - gameId + * properties: + * name: + * type: string + * gameId: + * type: string + * password: + * type: string + * nullable: true + * responses: + * 201: + * description: Chat created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Chat' + * + * /api/chats/message: + * post: + * tags: [Chats] + * summary: Send message + * description: Send a message to a chat + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - chatId + * - message + * properties: + * chatId: + * type: string + * message: + * type: string + * responses: + * 201: + * description: Message sent successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ChatMessage' + * + * /api/chats/archive/{chatId}: + * post: + * tags: [Chats] + * summary: Archive chat + * description: Archive a chat room + * security: + * - bearerAuth: [] + * parameters: + * - name: chatId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Chat archived successfully + * 404: + * description: Chat not found + * + * /api/chats/restore/{chatId}: + * post: + * tags: [Chats] + * summary: Restore chat + * description: Restore an archived chat room + * security: + * - bearerAuth: [] + * parameters: + * - name: chatId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Chat restored successfully + * 404: + * description: Chat not found + * + * /api/chats/archived/game/{gameId}: + * get: + * tags: [Chats] + * summary: Get archived chats by game + * description: Get all archived chats for a specific game + * security: + * - bearerAuth: [] + * parameters: + * - name: gameId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Archived chats + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Chat' + * + * /api/deck-import-export/export/{deckId}: + * get: + * tags: [Import/Export] + * summary: Export deck + * description: Export a deck as JSON or .spr file + * security: + * - bearerAuth: [] + * parameters: + * - name: deckId + * in: path + * required: true + * schema: + * type: string + * - name: format + * in: query + * schema: + * type: string + * enum: [json, spr] + * default: json + * responses: + * 200: + * description: Deck exported successfully + * content: + * application/json: + * schema: + * type: object + * application/octet-stream: + * schema: + * type: string + * format: binary + * + * /api/deck-import-export/import: + * post: + * tags: [Import/Export] + * summary: Import deck + * description: Import a deck from JSON or .spr file + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * properties: + * file: + * type: string + * format: binary + * responses: + * 201: + * description: Deck imported successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * + * /api/admin/users/page/{from}/{to}: + * get: + * tags: [Admin - Users] + * summary: Get users by page (Admin) + * description: Admin endpoint to retrieve paginated list of users + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated users + * content: + * application/json: + * schema: + * type: object + * properties: + * users: + * type: array + * items: + * $ref: '#/components/schemas/User' + * totalCount: + * type: integer + * + * /api/admin/users/{userId}: + * get: + * tags: [Admin - Users] + * summary: Get user by ID (Admin) + * description: Admin endpoint to get specific user details + * security: + * - bearerAuth: [] + * parameters: + * - name: userId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: User details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/User' + * delete: + * tags: [Admin - Users] + * summary: Delete user (Admin) + * description: Admin endpoint to delete a user + * security: + * - bearerAuth: [] + * parameters: + * - name: userId + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: User deleted successfully + * + * /api/admin/users/search/{searchTerm}: + * get: + * tags: [Admin - Users] + * summary: Search users (Admin) + * description: Admin endpoint to search users + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/User' + * + * /api/admin/users/{userId}/deactivate: + * post: + * tags: [Admin - Users] + * summary: Deactivate user (Admin) + * description: Admin endpoint to deactivate a user + * security: + * - bearerAuth: [] + * parameters: + * - name: userId + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: User deactivated successfully + * + * /api/admin/decks/page/{from}/{to}: + * get: + * tags: [Admin - Decks] + * summary: Get decks by page (Admin) + * description: Admin endpoint to retrieve paginated list of decks + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated decks + * content: + * application/json: + * schema: + * type: object + * properties: + * decks: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * totalCount: + * type: integer + * + * /api/admin/decks/{id}: + * get: + * tags: [Admin - Decks] + * summary: Get deck by ID (Admin) + * description: Admin endpoint to get specific deck details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Deck details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Deck' + * + * /api/admin/decks/search/{searchTerm}: + * get: + * tags: [Admin - Decks] + * summary: Search decks (Admin) + * description: Admin endpoint to search decks + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Deck' + * + * /api/admin/decks/{id}/hard: + * delete: + * tags: [Admin - Decks] + * summary: Hard delete deck (Admin) + * description: Admin endpoint to permanently delete a deck + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Deck permanently deleted + * + * /api/admin/organizations: + * post: + * tags: [Admin - Organizations] + * summary: Create organization (Admin) + * description: Admin endpoint to create a new organization + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * properties: + * name: + * type: string + * description: + * type: string + * responses: + * 201: + * description: Organization created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Organization' + * + * /api/admin/organizations/page/{from}/{to}: + * get: + * tags: [Admin - Organizations] + * summary: Get organizations by page (Admin) + * description: Admin endpoint to retrieve paginated list of organizations + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated organizations + * content: + * application/json: + * schema: + * type: object + * properties: + * organizations: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * totalCount: + * type: integer + * + * /api/admin/organizations/{id}: + * get: + * tags: [Admin - Organizations] + * summary: Get organization by ID (Admin) + * description: Admin endpoint to get specific organization details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Organization details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Organization' + * delete: + * tags: [Admin - Organizations] + * summary: Delete organization (Admin) + * description: Admin endpoint to soft delete an organization + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Organization deleted successfully + * + * /api/admin/organizations/search/{searchTerm}: + * get: + * tags: [Admin - Organizations] + * summary: Search organizations (Admin) + * description: Admin endpoint to search organizations + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * + * /api/admin/organizations/{id}/hard: + * delete: + * tags: [Admin - Organizations] + * summary: Hard delete organization (Admin) + * description: Admin endpoint to permanently delete an organization + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 204: + * description: Organization permanently deleted + * + * /api/admin/chats/page/{from}/{to}: + * get: + * tags: [Admin - Chats] + * summary: Get chats by page (Admin) + * description: Admin endpoint to retrieve paginated list of chats + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated chats + * content: + * application/json: + * schema: + * type: object + * properties: + * chats: + * type: array + * items: + * $ref: '#/components/schemas/Chat' + * totalCount: + * type: integer + * + * /api/admin/chats/{id}: + * get: + * tags: [Admin - Chats] + * summary: Get chat by ID (Admin) + * description: Admin endpoint to get specific chat details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Chat details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Chat' + * + * /api/admin/contacts/page/{from}/{to}: + * get: + * tags: [Admin - Contacts] + * summary: Get contacts by page (Admin) + * description: Admin endpoint to retrieve paginated list of contacts + * security: + * - bearerAuth: [] + * parameters: + * - name: from + * in: path + * required: true + * schema: + * type: integer + * - name: to + * in: path + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Paginated contacts + * content: + * application/json: + * schema: + * type: object + * properties: + * contacts: + * type: array + * items: + * $ref: '#/components/schemas/Contact' + * totalCount: + * type: integer + * + * /api/admin/contacts/{id}: + * get: + * tags: [Admin - Contacts] + * summary: Get contact by ID (Admin) + * description: Admin endpoint to get specific contact details + * security: + * - bearerAuth: [] + * parameters: + * - name: id + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Contact details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Contact' + * + * /api/admin/contacts/search/{searchTerm}: + * get: + * tags: [Admin - Contacts] + * summary: Search contacts (Admin) + * description: Admin endpoint to search contacts + * security: + * - bearerAuth: [] + * parameters: + * - name: searchTerm + * in: path + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Search results + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Contact' + * + * /api/contacts: + * post: + * tags: [Contacts] + * summary: Create contact + * description: Create a new contact message + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateContactRequest' + * responses: + * 201: + * description: Contact created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Contact' + */ +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=swaggerDefinitions.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.js.map b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.js.map new file mode 100644 index 00000000..0e80d3c0 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerDefinitions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"swaggerDefinitions.js","sourceRoot":"","sources":["../../../src/Api/swagger/swaggerDefinitions.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA81CG"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.d.ts b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.d.ts new file mode 100644 index 00000000..4a14beb8 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.d.ts @@ -0,0 +1,3 @@ +import express from 'express'; +export declare function setupSwagger(app: express.Application): void; +//# sourceMappingURL=swaggerUiSetup.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.d.ts.map b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.d.ts.map new file mode 100644 index 00000000..ab391802 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"swaggerUiSetup.d.ts","sourceRoot":"","sources":["../../../src/Api/swagger/swaggerUiSetup.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAI9B,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,QAEpD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.js b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.js new file mode 100644 index 00000000..a1541133 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.js @@ -0,0 +1,12 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setupSwagger = setupSwagger; +const swagger_ui_express_1 = __importDefault(require("swagger-ui-express")); +const swaggerConfig_1 = require("./swaggerConfig"); +function setupSwagger(app) { + app.use('/api-docs', swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(swaggerConfig_1.swaggerSpec)); +} +//# sourceMappingURL=swaggerUiSetup.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.js.map b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.js.map new file mode 100644 index 00000000..16589fe2 --- /dev/null +++ b/SerpentRace_Backend/dist/Api/swagger/swaggerUiSetup.js.map @@ -0,0 +1 @@ +{"version":3,"file":"swaggerUiSetup.js","sourceRoot":"","sources":["../../../src/Api/swagger/swaggerUiSetup.ts"],"names":[],"mappings":";;;;;AAIA,oCAEC;AALD,4EAA2C;AAC3C,mDAA8C;AAE9C,SAAgB,YAAY,CAAC,GAAwB;IACnD,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,4BAAS,CAAC,KAAK,EAAE,4BAAS,CAAC,KAAK,CAAC,2BAAW,CAAC,CAAC,CAAC;AACtE,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.d.ts b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.d.ts new file mode 100644 index 00000000..21a41381 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.d.ts @@ -0,0 +1,13 @@ +import { ArchiveChatCommand, RestoreChatCommand } from './ChatCommands'; +import { IChatRepository } from '../../../Domain/IRepository/IChatRepository'; +export declare class ArchiveChatCommandHandler { + private chatRepository; + constructor(chatRepository: IChatRepository); + execute(command: ArchiveChatCommand): Promise; +} +export declare class RestoreChatCommandHandler { + private chatRepository; + constructor(chatRepository: IChatRepository); + execute(command: RestoreChatCommand): Promise; +} +//# sourceMappingURL=ChatArchiveCommandHandlers.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.d.ts.map new file mode 100644 index 00000000..55adda6d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatArchiveCommandHandlers.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/ChatArchiveCommandHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAI9E,qBAAa,yBAAyB;IACtB,OAAO,CAAC,cAAc;gBAAd,cAAc,EAAE,eAAe;IAE7C,OAAO,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;CAsB/D;AAED,qBAAa,yBAAyB;IACtB,OAAO,CAAC,cAAc;gBAAd,cAAc,EAAE,eAAe;IAE7C,OAAO,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;CAiC/D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.js b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.js new file mode 100644 index 00000000..2c7ee234 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RestoreChatCommandHandler = exports.ArchiveChatCommandHandler = void 0; +const ChatAggregate_1 = require("../../../Domain/Chat/ChatAggregate"); +const Logger_1 = require("../../Services/Logger"); +class ArchiveChatCommandHandler { + constructor(chatRepository) { + this.chatRepository = chatRepository; + } + async execute(command) { + try { + const chat = await this.chatRepository.findById(command.chatId); + if (!chat) { + throw new Error('Chat not found'); + } + await this.chatRepository.archiveChat(chat); + (0, Logger_1.logAuth)('Chat archived manually', undefined, { + chatId: command.chatId, + chatType: chat.type, + messageCount: chat.messages.length + }); + return true; + } + catch (error) { + (0, Logger_1.logError)('ArchiveChatCommandHandler error', error); + return false; + } + } +} +exports.ArchiveChatCommandHandler = ArchiveChatCommandHandler; +class RestoreChatCommandHandler { + constructor(chatRepository) { + this.chatRepository = chatRepository; + } + async execute(command) { + try { + const archive = await this.chatRepository.getArchivedChat(command.chatId); + if (!archive) { + throw new Error('Archived chat not found'); + } + // Game chats cannot be restored, only viewed + if (archive.chatType === ChatAggregate_1.ChatType.GAME) { + (0, Logger_1.logWarning)('Attempt to restore game chat blocked', { + chatId: command.chatId, + chatType: archive.chatType + }); + return false; + } + const restoredChat = await this.chatRepository.restoreFromArchive(command.chatId); + if (!restoredChat) { + throw new Error('Failed to restore chat from archive'); + } + (0, Logger_1.logAuth)('Chat restored from archive', undefined, { + chatId: command.chatId, + messageCount: archive.archivedMessages.length + }); + return true; + } + catch (error) { + (0, Logger_1.logError)('RestoreChatCommandHandler error', error); + return false; + } + } +} +exports.RestoreChatCommandHandler = RestoreChatCommandHandler; +//# sourceMappingURL=ChatArchiveCommandHandlers.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.js.map b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.js.map new file mode 100644 index 00000000..f93b9380 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatArchiveCommandHandlers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatArchiveCommandHandlers.js","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/ChatArchiveCommandHandlers.ts"],"names":[],"mappings":";;;AAEA,sEAA8D;AAC9D,kDAAsE;AAEtE,MAAa,yBAAyB;IAClC,YAAoB,cAA+B;QAA/B,mBAAc,GAAd,cAAc,CAAiB;IAAG,CAAC;IAEvD,KAAK,CAAC,OAAO,CAAC,OAA2B;QACrC,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAChE,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACtC,CAAC;YAED,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAE5C,IAAA,gBAAO,EAAC,wBAAwB,EAAE,SAAS,EAAE;gBACzC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;aACrC,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QAEhB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAA,iBAAQ,EAAC,iCAAiC,EAAE,KAAc,CAAC,CAAC;YAC5D,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;CACJ;AAzBD,8DAyBC;AAED,MAAa,yBAAyB;IAClC,YAAoB,cAA+B;QAA/B,mBAAc,GAAd,cAAc,CAAiB;IAAG,CAAC;IAEvD,KAAK,CAAC,OAAO,CAAC,OAA2B;QACrC,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC1E,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC/C,CAAC;YAED,6CAA6C;YAC7C,IAAI,OAAO,CAAC,QAAQ,KAAK,wBAAQ,CAAC,IAAI,EAAE,CAAC;gBACrC,IAAA,mBAAU,EAAC,sCAAsC,EAAE;oBAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;iBAC7B,CAAC,CAAC;gBACH,OAAO,KAAK,CAAC;YACjB,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClF,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YAC3D,CAAC;YAED,IAAA,gBAAO,EAAC,4BAA4B,EAAE,SAAS,EAAE;gBAC7C,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,YAAY,EAAE,OAAO,CAAC,gBAAgB,CAAC,MAAM;aAChD,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QAEhB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAA,iBAAQ,EAAC,iCAAiC,EAAE,KAAc,CAAC,CAAC;YAC5D,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;CACJ;AApCD,8DAoCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.d.ts b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.d.ts new file mode 100644 index 00000000..da701535 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.d.ts @@ -0,0 +1,19 @@ +export interface CreateChatCommand { + type: 'direct' | 'group' | 'game'; + name?: string; + gameId?: string; + createdBy: string; + userIds: string[]; +} +export interface SendMessageCommand { + chatId: string; + userId: string; + message: string; +} +export interface ArchiveChatCommand { + chatId: string; +} +export interface RestoreChatCommand { + chatId: string; +} +//# sourceMappingURL=ChatCommands.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.d.ts.map new file mode 100644 index 00000000..a8f458da --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatCommands.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/ChatCommands.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAClB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.js b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.js new file mode 100644 index 00000000..fd9c2dae --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ChatCommands.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.js.map b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.js.map new file mode 100644 index 00000000..d964c636 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/ChatCommands.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatCommands.js","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/ChatCommands.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.d.ts new file mode 100644 index 00000000..9c3e1331 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.d.ts @@ -0,0 +1,11 @@ +import { CreateChatCommand } from './ChatCommands'; +import { IChatRepository } from '../../../Domain/IRepository/IChatRepository'; +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { ChatAggregate } from '../../../Domain/Chat/ChatAggregate'; +export declare class CreateChatCommandHandler { + private chatRepository; + private userRepository; + constructor(chatRepository: IChatRepository, userRepository: IUserRepository); + execute(command: CreateChatCommand): Promise; +} +//# sourceMappingURL=CreateChatCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.d.ts.map new file mode 100644 index 00000000..54e7f299 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateChatCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/CreateChatCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAY,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAI7E,qBAAa,wBAAwB;IAE7B,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,cAAc;gBADd,cAAc,EAAE,eAAe,EAC/B,cAAc,EAAE,eAAe;IAGrC,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CAuE3E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.js b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.js new file mode 100644 index 00000000..48a3460c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.js @@ -0,0 +1,71 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateChatCommandHandler = void 0; +const ChatAggregate_1 = require("../../../Domain/Chat/ChatAggregate"); +const UserAggregate_1 = require("../../../Domain/User/UserAggregate"); +const Logger_1 = require("../../Services/Logger"); +class CreateChatCommandHandler { + constructor(chatRepository, userRepository) { + this.chatRepository = chatRepository; + this.userRepository = userRepository; + } + async execute(command) { + try { + // Validate creator exists + const creator = await this.userRepository.findById(command.createdBy); + if (!creator) { + throw new Error('Creator not found'); + } + // For group chats, check if creator is premium + if (command.type === 'group' && creator.state !== UserAggregate_1.UserState.VERIFIED_PREMIUM) { + throw new Error('Premium subscription required to create groups'); + } + // Validate all target users exist + const targetUsers = await Promise.all(command.userIds.map(id => this.userRepository.findById(id))); + if (targetUsers.some(user => !user)) { + throw new Error('One or more target users not found'); + } + // For direct chats, check if already exists + if (command.type === 'direct' && command.userIds.length === 1) { + const existingChats = await this.chatRepository.findByUserId(command.createdBy); + const existingDirectChat = existingChats.find(chat => chat.type === ChatAggregate_1.ChatType.DIRECT && + chat.users.length === 2 && + chat.users.includes(command.userIds[0])); + if (existingDirectChat) { + return existingDirectChat; + } + } + // For game chats, check if already exists + if (command.type === 'game' && command.gameId) { + const existingGameChat = await this.chatRepository.findByGameId(command.gameId); + if (existingGameChat) { + return existingGameChat; + } + } + // Create chat + const chatData = { + type: command.type, + name: command.name, + gameId: command.gameId, + createdBy: command.createdBy, + users: [command.createdBy, ...command.userIds], + messages: [], + lastActivity: new Date() + }; + const chat = await this.chatRepository.create(chatData); + (0, Logger_1.logAuth)('Chat created successfully', command.createdBy, { + chatId: chat.id, + chatType: command.type, + participantCount: chat.users.length, + gameId: command.gameId + }); + return chat; + } + catch (error) { + (0, Logger_1.logError)('CreateChatCommandHandler error', error); + return null; + } + } +} +exports.CreateChatCommandHandler = CreateChatCommandHandler; +//# sourceMappingURL=CreateChatCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.js.map new file mode 100644 index 00000000..6a9cc580 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/CreateChatCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateChatCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/CreateChatCommandHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.d.ts new file mode 100644 index 00000000..55f64f92 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.d.ts @@ -0,0 +1,10 @@ +import { SendMessageCommand } from './ChatCommands'; +import { IChatRepository } from '../../../Domain/IRepository/IChatRepository'; +import { Message } from '../../../Domain/Chat/ChatAggregate'; +export declare class SendMessageCommandHandler { + private chatRepository; + constructor(chatRepository: IChatRepository); + execute(command: SendMessageCommand): Promise; + private pruneMessages; +} +//# sourceMappingURL=SendMessageCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.d.ts.map new file mode 100644 index 00000000..5a11e9cf --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SendMessageCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/SendMessageCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAI7D,qBAAa,yBAAyB;IACtB,OAAO,CAAC,cAAc;gBAAd,cAAc,EAAE,eAAe;IAE7C,OAAO,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAiDnE,OAAO,CAAC,aAAa;CAyBxB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.js b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.js new file mode 100644 index 00000000..8dd9d499 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.js @@ -0,0 +1,74 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SendMessageCommandHandler = void 0; +const Logger_1 = require("../../Services/Logger"); +const uuid_1 = require("uuid"); +class SendMessageCommandHandler { + constructor(chatRepository) { + this.chatRepository = chatRepository; + } + async execute(command) { + try { + // Validate message is non-empty string + if (typeof command.message !== 'string' || !command.message.trim()) { + throw new Error('Message must be a non-empty string'); + } + const chat = await this.chatRepository.findById(command.chatId); + if (!chat) { + throw new Error('Chat not found'); + } + // Check if user is member of this chat + if (!chat.users.includes(command.userId)) { + throw new Error('User is not a member of this chat'); + } + // Create message + const message = { + id: (0, uuid_1.v4)(), + date: new Date(), + userid: command.userId, + text: command.message.trim() + }; + // Manage message history (keep last 10 per user, up to 2 weeks) + let updatedMessages = [...chat.messages, message]; + updatedMessages = this.pruneMessages(updatedMessages); + // Update chat + await this.chatRepository.update(command.chatId, { + messages: updatedMessages, + lastActivity: new Date() + }); + (0, Logger_1.logAuth)('Message sent successfully', command.userId, { + chatId: command.chatId, + messageLength: command.message.length, + totalMessages: updatedMessages.length + }); + return message; + } + catch (error) { + (0, Logger_1.logError)('SendMessageCommandHandler error', error); + return null; + } + } + pruneMessages(messages) { + const twoWeeksAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000); + // Remove messages older than 2 weeks + let prunedMessages = messages.filter(msg => new Date(msg.date) > twoWeeksAgo); + // Group by user and keep last 10 messages per user + const messagesByUser = new Map(); + prunedMessages.forEach(msg => { + if (!messagesByUser.has(msg.userid)) { + messagesByUser.set(msg.userid, []); + } + messagesByUser.get(msg.userid).push(msg); + }); + // Keep only last 10 messages per user + const finalMessages = []; + messagesByUser.forEach((userMessages, userId) => { + const last10 = userMessages.slice(-10); + finalMessages.push(...last10); + }); + // Sort by date + return finalMessages.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + } +} +exports.SendMessageCommandHandler = SendMessageCommandHandler; +//# sourceMappingURL=SendMessageCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.js.map new file mode 100644 index 00000000..495c34bc --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SendMessageCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SendMessageCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/SendMessageCommandHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.d.ts b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.d.ts new file mode 100644 index 00000000..8291e50f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.d.ts @@ -0,0 +1 @@ +//# sourceMappingURL=SoftDeleteCommandHandlers.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.d.ts.map new file mode 100644 index 00000000..877f04d0 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SoftDeleteCommandHandlers.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/SoftDeleteCommandHandlers.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.js b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.js new file mode 100644 index 00000000..ecfdffa3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.js @@ -0,0 +1,2 @@ +"use strict"; +//# sourceMappingURL=SoftDeleteCommandHandlers.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.js.map b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.js.map new file mode 100644 index 00000000..269abadf --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/commands/SoftDeleteCommandHandlers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SoftDeleteCommandHandlers.js","sourceRoot":"","sources":["../../../../src/Application/Chat/commands/SoftDeleteCommandHandlers.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.d.ts b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.d.ts new file mode 100644 index 00000000..812acfbb --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.d.ts @@ -0,0 +1,28 @@ +import { GetChatHistoryQuery, GetArchivedChatsQuery } from './ChatQueries'; +import { IChatRepository } from '../../../Domain/IRepository/IChatRepository'; +import { IChatArchiveRepository } from '../../../Domain/IRepository/IChatArchiveRepository'; +import { Message } from '../../../Domain/Chat/ChatAggregate'; +interface ChatHistoryResult { + chatId: string; + messages: Message[]; + isArchived: boolean; + chatInfo: { + type: string; + name: string | null; + gameId: string | null; + users: string[]; + }; +} +export declare class GetChatHistoryQueryHandler { + private chatRepository; + private chatArchiveRepository; + constructor(chatRepository: IChatRepository, chatArchiveRepository: IChatArchiveRepository); + execute(query: GetChatHistoryQuery): Promise; +} +export declare class GetArchivedChatsQueryHandler { + private chatArchiveRepository; + constructor(chatArchiveRepository: IChatArchiveRepository); + execute(query: GetArchivedChatsQuery): Promise; +} +export {}; +//# sourceMappingURL=ChatHistoryQueryHandlers.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.d.ts.map new file mode 100644 index 00000000..a374f29b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatHistoryQueryHandlers.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/ChatHistoryQueryHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAG7D,UAAU,iBAAiB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,KAAK,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;CACL;AAED,qBAAa,0BAA0B;IAE/B,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,qBAAqB;gBADrB,cAAc,EAAE,eAAe,EAC/B,qBAAqB,EAAE,sBAAsB;IAGnD,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CAwE/E;AAED,qBAAa,4BAA4B;IACzB,OAAO,CAAC,qBAAqB;gBAArB,qBAAqB,EAAE,sBAAsB;IAE3D,OAAO,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;CAuC5E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.js b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.js new file mode 100644 index 00000000..c9f6106d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.js @@ -0,0 +1,116 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetArchivedChatsQueryHandler = exports.GetChatHistoryQueryHandler = void 0; +const Logger_1 = require("../../Services/Logger"); +class GetChatHistoryQueryHandler { + constructor(chatRepository, chatArchiveRepository) { + this.chatRepository = chatRepository; + this.chatArchiveRepository = chatArchiveRepository; + } + async execute(query) { + try { + // First try to find active chat + const chat = await this.chatRepository.findById(query.chatId); + if (chat) { + // Check authorization + if (!chat.users.includes(query.userId)) { + (0, Logger_1.logWarning)('Unauthorized chat history access attempt', { + chatId: query.chatId, + userId: query.userId + }); + return null; + } + (0, Logger_1.logAuth)('Chat history retrieved', query.userId, { + chatId: query.chatId, + messageCount: chat.messages.length, + isArchived: false + }); + return { + chatId: query.chatId, + messages: chat.messages, + isArchived: false, + chatInfo: { + type: chat.type, + name: chat.name, + gameId: chat.gameId, + users: chat.users + } + }; + } + // Try to find in archives + const archives = await this.chatArchiveRepository.findByChatId(query.chatId); + const userArchive = archives.find(archive => archive.participants.includes(query.userId)); + if (userArchive) { + (0, Logger_1.logAuth)('Archived chat history retrieved', query.userId, { + chatId: query.chatId, + messageCount: userArchive.archivedMessages.length, + isArchived: true + }); + return { + chatId: query.chatId, + messages: userArchive.archivedMessages, + isArchived: true, + chatInfo: { + type: userArchive.chatType, + name: userArchive.chatName, + gameId: userArchive.gameId, + users: userArchive.participants + } + }; + } + (0, Logger_1.logWarning)('Chat history not found', { + chatId: query.chatId, + userId: query.userId + }); + return null; + } + catch (error) { + (0, Logger_1.logError)('GetChatHistoryQueryHandler error', error); + return null; + } + } +} +exports.GetChatHistoryQueryHandler = GetChatHistoryQueryHandler; +class GetArchivedChatsQueryHandler { + constructor(chatArchiveRepository) { + this.chatArchiveRepository = chatArchiveRepository; + } + async execute(query) { + try { + let archives = []; + if (query.gameId) { + // Get archived game chats + archives = await this.chatArchiveRepository.findByGameId(query.gameId); + } + else { + // Get all archived chats for user (would need different query) + // For now, return empty - this would need a new repository method + archives = []; + } + const result = archives + .filter(archive => archive.participants.includes(query.userId)) + .map(archive => ({ + chatId: archive.chatId, + messages: archive.archivedMessages, + isArchived: true, + chatInfo: { + type: archive.chatType, + name: archive.chatName, + gameId: archive.gameId, + users: archive.participants + } + })); + (0, Logger_1.logAuth)('Archived chats retrieved', query.userId, { + count: result.length, + gameId: query.gameId + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('GetArchivedChatsQueryHandler error', error); + return []; + } + } +} +exports.GetArchivedChatsQueryHandler = GetArchivedChatsQueryHandler; +//# sourceMappingURL=ChatHistoryQueryHandlers.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.js.map b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.js.map new file mode 100644 index 00000000..35e52566 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatHistoryQueryHandlers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatHistoryQueryHandlers.js","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/ChatHistoryQueryHandlers.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.d.ts b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.d.ts new file mode 100644 index 00000000..7244b852 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.d.ts @@ -0,0 +1,13 @@ +export interface GetUserChatsQuery { + userId: string; + includeArchived?: boolean; +} +export interface GetChatHistoryQuery { + chatId: string; + userId: string; +} +export interface GetArchivedChatsQuery { + userId: string; + gameId?: string; +} +//# sourceMappingURL=ChatQueries.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.d.ts.map new file mode 100644 index 00000000..245cf578 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatQueries.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/ChatQueries.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.js b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.js new file mode 100644 index 00000000..622e755b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ChatQueries.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.js.map b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.js.map new file mode 100644 index 00000000..4b5a3201 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/ChatQueries.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatQueries.js","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/ChatQueries.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.d.ts b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.d.ts new file mode 100644 index 00000000..31fcd951 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.d.ts @@ -0,0 +1,6 @@ +export interface GetChatsByPageQuery { + from: number; + to: number; + includeDeleted?: boolean; +} +//# sourceMappingURL=GetChatsByPageQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.d.ts.map new file mode 100644 index 00000000..ca227a8d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetChatsByPageQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/GetChatsByPageQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.js b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.js new file mode 100644 index 00000000..dfd56cff --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetChatsByPageQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.js.map b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.js.map new file mode 100644 index 00000000..9c361c58 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetChatsByPageQuery.js","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/GetChatsByPageQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.d.ts new file mode 100644 index 00000000..cb055057 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.d.ts @@ -0,0 +1,12 @@ +import { IChatRepository } from '../../../Domain/IRepository/IChatRepository'; +import { GetChatsByPageQuery } from './GetChatsByPageQuery'; +import { ShortChatDto } from '../../DTOs/ChatDto'; +export declare class GetChatsByPageQueryHandler { + private readonly chatRepo; + constructor(chatRepo: IChatRepository); + execute(query: GetChatsByPageQuery): Promise<{ + chats: ShortChatDto[]; + totalCount: number; + }>; +} +//# sourceMappingURL=GetChatsByPageQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.d.ts.map new file mode 100644 index 00000000..679c0718 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetChatsByPageQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/GetChatsByPageQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CA6ClG"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.js b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.js new file mode 100644 index 00000000..ccea6d95 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetChatsByPageQueryHandler = void 0; +const ChatMapper_1 = require("../../DTOs/Mappers/ChatMapper"); +const Logger_1 = require("../../Services/Logger"); +class GetChatsByPageQueryHandler { + constructor(chatRepo) { + this.chatRepo = chatRepo; + } + async execute(query) { + try { + // Validate pagination parameters + if (query.from < 0 || query.to < query.from) { + throw new Error('Invalid pagination parameters'); + } + const limit = query.to - query.from + 1; + if (limit > 100) { + throw new Error('Page size too large. Maximum 100 records per request'); + } + (0, Logger_1.logRequest)('Get chats by page query started', undefined, undefined, { + from: query.from, + to: query.to, + includeDeleted: query.includeDeleted || false + }); + const result = query.includeDeleted + ? await this.chatRepo.findByPageIncludingDeleted(query.from, query.to) + : await this.chatRepo.findByPage(query.from, query.to); + (0, Logger_1.logRequest)('Get chats by page query completed', undefined, undefined, { + from: query.from, + to: query.to, + returned: result.chats.length, + totalCount: result.totalCount, + includeDeleted: query.includeDeleted || false + }); + return { + chats: ChatMapper_1.ChatMapper.toShortDtoList(result.chats), + totalCount: result.totalCount + }; + } + catch (error) { + (0, Logger_1.logError)('GetChatsByPageQueryHandler error', error instanceof Error ? error : new Error(String(error))); + // Re-throw validation errors as-is + if (error instanceof Error && (error.message.includes('Invalid pagination') || error.message.includes('Page size'))) { + throw error; + } + throw new Error('Failed to retrieve chats page'); + } + } +} +exports.GetChatsByPageQueryHandler = GetChatsByPageQueryHandler; +//# sourceMappingURL=GetChatsByPageQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.js.map new file mode 100644 index 00000000..9ada84b1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetChatsByPageQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetChatsByPageQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/GetChatsByPageQueryHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAC3D,kDAA6D;AAE7D,MAAa,0BAA0B;IACrC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,KAA0B;QACtC,IAAI,CAAC;YACH,iCAAiC;YACjC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YACxC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;YAC1E,CAAC;YAED,IAAA,mBAAU,EAAC,iCAAiC,EAAE,SAAS,EAAE,SAAS,EAAE;gBAClE,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;aAC9C,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc;gBACjC,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACtE,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAEzD,IAAA,mBAAU,EAAC,mCAAmC,EAAE,SAAS,EAAE,SAAS,EAAE;gBACpE,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;gBAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;aAC9C,CAAC,CAAC;YAEH,OAAO;gBACL,KAAK,EAAE,uBAAU,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC9C,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,kCAAkC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAExG,mCAAmC;YACnC,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBACpH,MAAM,KAAK,CAAC;YACd,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;CACF;AAhDD,gEAgDC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.d.ts new file mode 100644 index 00000000..3bcfcddd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.d.ts @@ -0,0 +1,23 @@ +import { GetUserChatsQuery } from './ChatQueries'; +import { IChatRepository } from '../../../Domain/IRepository/IChatRepository'; +import { IChatArchiveRepository } from '../../../Domain/IRepository/IChatArchiveRepository'; +interface ChatWithMetadata { + id: string; + type: string; + name: string | null; + gameId: string | null; + users: string[]; + lastActivity: Date | null; + isArchived: boolean; + messageCount: number; + unreadCount?: number; +} +export declare class GetUserChatsQueryHandler { + private chatRepository; + private chatArchiveRepository; + constructor(chatRepository: IChatRepository, chatArchiveRepository: IChatArchiveRepository); + execute(query: GetUserChatsQuery): Promise; + private calculateUnreadMessages; +} +export {}; +//# sourceMappingURL=GetUserChatsQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.d.ts.map new file mode 100644 index 00000000..5b3079de --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUserChatsQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/GetUserChatsQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AAK5F,UAAU,gBAAgB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,wBAAwB;IAE7B,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,qBAAqB;gBADrB,cAAc,EAAE,eAAe,EAC/B,qBAAqB,EAAE,sBAAsB;IAGnD,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAkEpE,OAAO,CAAC,uBAAuB;CAKlC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.js b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.js new file mode 100644 index 00000000..d302dd51 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.js @@ -0,0 +1,76 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetUserChatsQueryHandler = void 0; +const Logger_1 = require("../../Services/Logger"); +class GetUserChatsQueryHandler { + constructor(chatRepository, chatArchiveRepository) { + this.chatRepository = chatRepository; + this.chatArchiveRepository = chatArchiveRepository; + } + async execute(query) { + try { + const result = []; + // Get active chats + const activeChats = await this.chatRepository.findActiveChatsForUser(query.userId); + result.push(...activeChats.map(chat => ({ + id: chat.id, + type: chat.type, + name: chat.name, + gameId: chat.gameId, + users: chat.users, + lastActivity: chat.lastActivity, + isArchived: false, + messageCount: chat.messages.length, + unreadCount: this.calculateUnreadMessages(chat, query.userId) + }))); + // Get archived chats if requested + if (query.includeArchived) { + const userActiveChats = await this.chatRepository.findByUserId(query.userId); + const archivedChatIds = userActiveChats + .filter(chat => chat.archiveDate !== null) + .map(chat => chat.id); + const archives = await Promise.all(archivedChatIds.map(id => this.chatArchiveRepository.findByChatId(id))); + archives.forEach(archiveArray => { + archiveArray.forEach(archive => { + if (archive.participants.includes(query.userId)) { + result.push({ + id: archive.chatId, + type: archive.chatType, + name: archive.chatName, + gameId: archive.gameId, + users: archive.participants, + lastActivity: archive.archivedAt, + isArchived: true, + messageCount: archive.archivedMessages.length, + unreadCount: 0 // Archived chats have no unread messages + }); + } + }); + }); + } + (0, Logger_1.logAuth)('User chats retrieved', query.userId, { + activeCount: activeChats.length, + totalCount: result.length, + includeArchived: query.includeArchived + }); + return result.sort((a, b) => { + if (!a.lastActivity) + return 1; + if (!b.lastActivity) + return -1; + return new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime(); + }); + } + catch (error) { + (0, Logger_1.logError)('GetUserChatsQueryHandler error', error); + return []; + } + } + calculateUnreadMessages(chat, userId) { + // Simple implementation - count messages from other users + // In production, you'd store lastSeen timestamp per user per chat + return chat.messages.filter(msg => msg.userid !== userId).length; + } +} +exports.GetUserChatsQueryHandler = GetUserChatsQueryHandler; +//# sourceMappingURL=GetUserChatsQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.js.map new file mode 100644 index 00000000..b939397f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Chat/queries/GetUserChatsQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUserChatsQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Chat/queries/GetUserChatsQueryHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.d.ts b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.d.ts new file mode 100644 index 00000000..33515c4f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.d.ts @@ -0,0 +1,9 @@ +import { ContactType } from '../../../Domain/Contact/ContactAggregate'; +export interface CreateContactCommand { + name: string; + email: string; + userid?: string; + type: ContactType; + txt: string; +} +//# sourceMappingURL=CreateContactCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.d.ts.map new file mode 100644 index 00000000..38bac24a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateContactCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/CreateContactCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.js b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.js new file mode 100644 index 00000000..8d0ca702 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=CreateContactCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.js.map b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.js.map new file mode 100644 index 00000000..8beee645 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateContactCommand.js","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/CreateContactCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.d.ts new file mode 100644 index 00000000..57d16361 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { IContactRepository } from '../../../Domain/IRepository/IContactRepository'; +import { CreateContactCommand } from './CreateContactCommand'; +import { ShortContactDto } from '../../DTOs/ContactDto'; +export declare class CreateContactCommandHandler { + private readonly contactRepo; + constructor(contactRepo: IContactRepository); + execute(cmd: CreateContactCommand): Promise; +} +//# sourceMappingURL=CreateContactCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.d.ts.map new file mode 100644 index 00000000..69befbef --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateContactCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/CreateContactCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAIxD,qBAAa,2BAA2B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,kBAAkB;IAEtD,OAAO,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;CAgBnE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.js b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.js new file mode 100644 index 00000000..279334d9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateContactCommandHandler = void 0; +const ContactAggregate_1 = require("../../../Domain/Contact/ContactAggregate"); +const ContactMapper_1 = require("../../DTOs/Mappers/ContactMapper"); +class CreateContactCommandHandler { + constructor(contactRepo) { + this.contactRepo = contactRepo; + } + async execute(cmd) { + try { + const contact = new ContactAggregate_1.ContactAggregate(); + contact.name = cmd.name; + contact.email = cmd.email; + contact.userid = cmd.userid || null; + contact.type = cmd.type; + contact.txt = cmd.txt; + contact.state = ContactAggregate_1.ContactState.ACTIVE; + const created = await this.contactRepo.create(contact); + return ContactMapper_1.ContactMapper.toShortDto(created); + } + catch (error) { + throw new Error('Failed to create contact'); + } + } +} +exports.CreateContactCommandHandler = CreateContactCommandHandler; +//# sourceMappingURL=CreateContactCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.js.map new file mode 100644 index 00000000..1928b324 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/CreateContactCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateContactCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/CreateContactCommandHandler.ts"],"names":[],"mappings":";;;AAGA,+EAA0F;AAC1F,oEAAiE;AAEjE,MAAa,2BAA2B;IACtC,YAA6B,WAA+B;QAA/B,gBAAW,GAAX,WAAW,CAAoB;IAAG,CAAC;IAEhE,KAAK,CAAC,OAAO,CAAC,GAAyB;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,mCAAgB,EAAE,CAAC;YACvC,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YACxB,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YAC1B,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC;YACpC,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YACxB,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;YACtB,OAAO,CAAC,KAAK,GAAG,+BAAY,CAAC,MAAM,CAAC;YAEpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACvD,OAAO,6BAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;CACF;AAnBD,kEAmBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.d.ts b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.d.ts new file mode 100644 index 00000000..a393bfbd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.d.ts @@ -0,0 +1,5 @@ +export interface DeleteContactCommand { + id: string; + hard?: boolean; +} +//# sourceMappingURL=DeleteContactCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.d.ts.map new file mode 100644 index 00000000..2103d80f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteContactCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/DeleteContactCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.js b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.js new file mode 100644 index 00000000..46d8a55f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=DeleteContactCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.js.map b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.js.map new file mode 100644 index 00000000..7f62eba9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteContactCommand.js","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/DeleteContactCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.d.ts new file mode 100644 index 00000000..a377da0b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { IContactRepository } from '../../../Domain/IRepository/IContactRepository'; +import { DeleteContactCommand } from './DeleteContactCommand'; +export declare class DeleteContactCommandHandler { + private readonly contactRepo; + constructor(contactRepo: IContactRepository); + execute(cmd: DeleteContactCommand): Promise; +} +//# sourceMappingURL=DeleteContactCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.d.ts.map new file mode 100644 index 00000000..07006824 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteContactCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/DeleteContactCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAI9D,qBAAa,2BAA2B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,kBAAkB;IAEtD,OAAO,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;CAiC3D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.js b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.js new file mode 100644 index 00000000..40c8f7e5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.js @@ -0,0 +1,44 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteContactCommandHandler = void 0; +const Logger_1 = require("../../Services/Logger"); +class DeleteContactCommandHandler { + constructor(contactRepo) { + this.contactRepo = contactRepo; + } + async execute(cmd) { + try { + const existingContact = await this.contactRepo.findById(cmd.id); + if (!existingContact) { + throw new Error('Contact not found'); + } + if (cmd.hard) { + // Permanent delete + await this.contactRepo.delete(cmd.id); + (0, Logger_1.logRequest)('Contact hard deleted', undefined, undefined, { + contactId: cmd.id, + contactEmail: existingContact.email, + deleteType: 'hard' + }); + } + else { + // Soft delete (default) + await this.contactRepo.softDelete(cmd.id); + (0, Logger_1.logRequest)('Contact soft deleted', undefined, undefined, { + contactId: cmd.id, + contactEmail: existingContact.email, + deleteType: 'soft' + }); + } + return true; + } + catch (error) { + if (error instanceof Error && error.message === 'Contact not found') { + throw error; + } + throw new Error('Failed to delete contact'); + } + } +} +exports.DeleteContactCommandHandler = DeleteContactCommandHandler; +//# sourceMappingURL=DeleteContactCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.js.map new file mode 100644 index 00000000..88d2104e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/DeleteContactCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteContactCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/DeleteContactCommandHandler.ts"],"names":[],"mappings":";;;AAGA,kDAAmD;AAEnD,MAAa,2BAA2B;IACtC,YAA6B,WAA+B;QAA/B,gBAAW,GAAX,WAAW,CAAoB;IAAG,CAAC;IAEhE,KAAK,CAAC,OAAO,CAAC,GAAyB;QACrC,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;YAED,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;gBACb,mBAAmB;gBACnB,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACtC,IAAA,mBAAU,EAAC,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE;oBACvD,SAAS,EAAE,GAAG,CAAC,EAAE;oBACjB,YAAY,EAAE,eAAe,CAAC,KAAK;oBACnC,UAAU,EAAE,MAAM;iBACnB,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,wBAAwB;gBACxB,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC1C,IAAA,mBAAU,EAAC,sBAAsB,EAAE,SAAS,EAAE,SAAS,EAAE;oBACvD,SAAS,EAAE,GAAG,CAAC,EAAE;oBACjB,YAAY,EAAE,eAAe,CAAC,KAAK;oBACnC,UAAU,EAAE,MAAM;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,mBAAmB,EAAE,CAAC;gBACpE,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;CACF;AApCD,kEAoCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.d.ts b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.d.ts new file mode 100644 index 00000000..ca3a9fe8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.d.ts @@ -0,0 +1,7 @@ +export interface UpdateContactCommand { + id: string; + adminResponse?: string; + state?: number; + respondedBy?: string; +} +//# sourceMappingURL=UpdateContactCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.d.ts.map new file mode 100644 index 00000000..7c55ddfa --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateContactCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/UpdateContactCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.js b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.js new file mode 100644 index 00000000..a451a3de --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=UpdateContactCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.js.map b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.js.map new file mode 100644 index 00000000..e0d3eb7a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateContactCommand.js","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/UpdateContactCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.d.ts new file mode 100644 index 00000000..c594958d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { IContactRepository } from '../../../Domain/IRepository/IContactRepository'; +import { UpdateContactCommand } from './UpdateContactCommand'; +import { DetailContactDto } from '../../DTOs/ContactDto'; +export declare class UpdateContactCommandHandler { + private readonly contactRepo; + constructor(contactRepo: IContactRepository); + execute(cmd: UpdateContactCommand): Promise; +} +//# sourceMappingURL=UpdateContactCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.d.ts.map new file mode 100644 index 00000000..8ebe1611 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateContactCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/UpdateContactCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD,qBAAa,2BAA2B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,kBAAkB;IAEtD,OAAO,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAmCpE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.js b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.js new file mode 100644 index 00000000..8a8232ab --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.js @@ -0,0 +1,41 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateContactCommandHandler = void 0; +const ContactMapper_1 = require("../../DTOs/Mappers/ContactMapper"); +class UpdateContactCommandHandler { + constructor(contactRepo) { + this.contactRepo = contactRepo; + } + async execute(cmd) { + try { + const existingContact = await this.contactRepo.findById(cmd.id); + if (!existingContact) { + throw new Error('Contact not found'); + } + const updateData = {}; + if (cmd.adminResponse !== undefined) { + updateData.adminResponse = cmd.adminResponse; + updateData.responseDate = new Date(); + } + if (cmd.state !== undefined) { + updateData.state = cmd.state; + } + if (cmd.respondedBy !== undefined) { + updateData.respondedBy = cmd.respondedBy; + } + const updated = await this.contactRepo.update(cmd.id, updateData); + if (!updated) { + throw new Error('Failed to update contact'); + } + return ContactMapper_1.ContactMapper.toDetailDto(updated); + } + catch (error) { + if (error instanceof Error && error.message === 'Contact not found') { + throw error; + } + throw new Error('Failed to update contact'); + } + } +} +exports.UpdateContactCommandHandler = UpdateContactCommandHandler; +//# sourceMappingURL=UpdateContactCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.js.map new file mode 100644 index 00000000..8b894d25 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/commands/UpdateContactCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateContactCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Contact/commands/UpdateContactCommandHandler.ts"],"names":[],"mappings":";;;AAGA,oEAAiE;AAGjE,MAAa,2BAA2B;IACtC,YAA6B,WAA+B;QAA/B,gBAAW,GAAX,WAAW,CAAoB;IAAG,CAAC;IAEhE,KAAK,CAAC,OAAO,CAAC,GAAyB;QACrC,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;YAED,MAAM,UAAU,GAAQ,EAAE,CAAC;YAE3B,IAAI,GAAG,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;gBACpC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC;gBAC7C,UAAU,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;YACvC,CAAC;YAED,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC5B,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;YAC/B,CAAC;YAED,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBAClC,UAAU,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;YAC3C,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAClE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAC9C,CAAC;YAED,OAAO,6BAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,mBAAmB,EAAE,CAAC;gBACpE,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;CACF;AAtCD,kEAsCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.d.ts b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.d.ts new file mode 100644 index 00000000..f6269910 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.d.ts @@ -0,0 +1,4 @@ +export interface GetContactByIdQuery { + id: string; +} +//# sourceMappingURL=GetContactByIdQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.d.ts.map new file mode 100644 index 00000000..7e9b1a87 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactByIdQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactByIdQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.js b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.js new file mode 100644 index 00000000..c059abbf --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetContactByIdQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.js.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.js.map new file mode 100644 index 00000000..5ef049e8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactByIdQuery.js","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactByIdQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.d.ts new file mode 100644 index 00000000..b781ba6a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.d.ts @@ -0,0 +1,9 @@ +import { IContactRepository } from '../../../Domain/IRepository/IContactRepository'; +import { GetContactByIdQuery } from './GetContactByIdQuery'; +import { DetailContactDto } from '../../DTOs/ContactDto'; +export declare class GetContactByIdQueryHandler { + private readonly contactRepo; + constructor(contactRepo: IContactRepository); + execute(query: GetContactByIdQuery): Promise; +} +//# sourceMappingURL=GetContactByIdQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.d.ts.map new file mode 100644 index 00000000..34d4626f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactByIdQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactByIdQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,kBAAkB;IAEtD,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;CAO5E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.js b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.js new file mode 100644 index 00000000..d4c39503 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetContactByIdQueryHandler = void 0; +const ContactMapper_1 = require("../../DTOs/Mappers/ContactMapper"); +class GetContactByIdQueryHandler { + constructor(contactRepo) { + this.contactRepo = contactRepo; + } + async execute(query) { + const contact = await this.contactRepo.findById(query.id); + if (!contact) { + return null; + } + return ContactMapper_1.ContactMapper.toDetailDto(contact); + } +} +exports.GetContactByIdQueryHandler = GetContactByIdQueryHandler; +//# sourceMappingURL=GetContactByIdQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.js.map new file mode 100644 index 00000000..664977df --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactByIdQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactByIdQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactByIdQueryHandler.ts"],"names":[],"mappings":";;;AAGA,oEAAiE;AAEjE,MAAa,0BAA0B;IACrC,YAA6B,WAA+B;QAA/B,gBAAW,GAAX,WAAW,CAAoB;IAAG,CAAC;IAEhE,KAAK,CAAC,OAAO,CAAC,KAA0B;QACtC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,6BAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;CACF;AAVD,gEAUC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.d.ts b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.d.ts new file mode 100644 index 00000000..75fee6bd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.d.ts @@ -0,0 +1,5 @@ +export interface GetContactsByPageQuery { + from: number; + to: number; +} +//# sourceMappingURL=GetContactsByPageQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.d.ts.map new file mode 100644 index 00000000..4de84935 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactsByPageQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactsByPageQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.js b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.js new file mode 100644 index 00000000..bbe184c4 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetContactsByPageQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.js.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.js.map new file mode 100644 index 00000000..99198905 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactsByPageQuery.js","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactsByPageQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.d.ts new file mode 100644 index 00000000..f6f06744 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.d.ts @@ -0,0 +1,9 @@ +import { IContactRepository } from '../../../Domain/IRepository/IContactRepository'; +import { GetContactsByPageQuery } from './GetContactsByPageQuery'; +import { ContactPageDto } from '../../DTOs/ContactDto'; +export declare class GetContactsByPageQueryHandler { + private readonly contactRepo; + constructor(contactRepo: IContactRepository); + execute(query: GetContactsByPageQuery): Promise; +} +//# sourceMappingURL=GetContactsByPageQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.d.ts.map new file mode 100644 index 00000000..a9173b8d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactsByPageQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactsByPageQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,qBAAa,6BAA6B;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,kBAAkB;IAEtD,OAAO,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;CAStE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.js b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.js new file mode 100644 index 00000000..dbf44f1c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetContactsByPageQueryHandler = void 0; +const ContactMapper_1 = require("../../DTOs/Mappers/ContactMapper"); +class GetContactsByPageQueryHandler { + constructor(contactRepo) { + this.contactRepo = contactRepo; + } + async execute(query) { + const result = await this.contactRepo.findByPage(query.from, query.to); + return { + contacts: ContactMapper_1.ContactMapper.toShortDtoList(result.contacts), + totalCount: result.totalCount, + from: query.from, + to: query.to, + }; + } +} +exports.GetContactsByPageQueryHandler = GetContactsByPageQueryHandler; +//# sourceMappingURL=GetContactsByPageQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.js.map new file mode 100644 index 00000000..06996c63 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Contact/queries/GetContactsByPageQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetContactsByPageQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Contact/queries/GetContactsByPageQueryHandler.ts"],"names":[],"mappings":";;;AAGA,oEAAiE;AAEjE,MAAa,6BAA6B;IACxC,YAA6B,WAA+B;QAA/B,gBAAW,GAAX,WAAW,CAAoB;IAAG,CAAC;IAEhE,KAAK,CAAC,OAAO,CAAC,KAA6B;QACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACvE,OAAO;YACL,QAAQ,EAAE,6BAAa,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC;YACvD,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,EAAE,EAAE,KAAK,CAAC,EAAE;SACb,CAAC;IACJ,CAAC;CACF;AAZD,sEAYC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ChatDto.d.ts b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.d.ts new file mode 100644 index 00000000..856a5853 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.d.ts @@ -0,0 +1,24 @@ +export interface CreateChatDto { + users: string[]; + messages: import('../../Domain/Chat/ChatAggregate').Message[]; + state?: number; +} +export interface UpdateChatDto { + id: string; + users?: string[]; + messages?: import('../../Domain/Chat/ChatAggregate').Message[]; + state?: number; +} +export interface ShortChatDto { + id: string; + userCount: number; + state: number; +} +export interface DetailChatDto { + id: string; + users: string[]; + messages: import('../../Domain/Chat/ChatAggregate').Message[]; + updateDate: Date; + state: number; +} +//# sourceMappingURL=ChatDto.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ChatDto.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.d.ts.map new file mode 100644 index 00000000..08d7153c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatDto.d.ts","sourceRoot":"","sources":["../../../src/Application/DTOs/ChatDto.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,iCAAiC,EAAE,OAAO,EAAE,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,iCAAiC,EAAE,OAAO,EAAE,CAAC;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,iCAAiC,EAAE,OAAO,EAAE,CAAC;IAC9D,UAAU,EAAE,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ChatDto.js b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.js new file mode 100644 index 00000000..66612d4c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ChatDto.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ChatDto.js.map b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.js.map new file mode 100644 index 00000000..9ad2a09e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ChatDto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatDto.js","sourceRoot":"","sources":["../../../src/Application/DTOs/ChatDto.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ContactDto.d.ts b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.d.ts new file mode 100644 index 00000000..76642bb7 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.d.ts @@ -0,0 +1,43 @@ +import { ContactType } from '../../Domain/Contact/ContactAggregate'; +export interface CreateContactDto { + name: string; + email: string; + userid?: string; + type: ContactType; + txt: string; +} +export interface UpdateContactDto { + id: string; + adminResponse?: string; + state?: number; + respondedBy?: string; +} +export interface ShortContactDto { + id: string; + name: string; + email: string; + type: ContactType; + createDate: Date; + state: number; +} +export interface DetailContactDto { + id: string; + name: string; + email: string; + userid: string | null; + type: ContactType; + txt: string; + state: number; + createDate: Date; + updateDate: Date; + adminResponse: string | null; + responseDate: Date | null; + respondedBy: string | null; +} +export interface ContactPageDto { + contacts: ShortContactDto[]; + totalCount: number; + from: number; + to: number; +} +//# sourceMappingURL=ContactDto.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ContactDto.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.d.ts.map new file mode 100644 index 00000000..5153996f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactDto.d.ts","sourceRoot":"","sources":["../../../src/Application/DTOs/ContactDto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,IAAI,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ContactDto.js b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.js new file mode 100644 index 00000000..19a65016 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ContactDto.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/ContactDto.js.map b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.js.map new file mode 100644 index 00000000..07c89b6f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/ContactDto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactDto.js","sourceRoot":"","sources":["../../../src/Application/DTOs/ContactDto.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/DeckDto.d.ts b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.d.ts new file mode 100644 index 00000000..f5f8c84e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.d.ts @@ -0,0 +1,27 @@ +export interface CreateDeckDto { + name: string; + description?: string; +} +export interface UpdateDeckDto { + id: string; + name?: string; + description?: string; +} +export interface ShortDeckDto { + id: string; + name: string; + type: number; + playedNumber: number; + ctype: number; +} +export interface DetailDeckDto { + id: string; + name: string; + type: number; + userid: string; + creationdate: Date; + cards: any[]; + playedNumber: number; + ctype: number; +} +//# sourceMappingURL=DeckDto.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/DeckDto.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.d.ts.map new file mode 100644 index 00000000..57609901 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckDto.d.ts","sourceRoot":"","sources":["../../../src/Application/DTOs/DeckDto.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,IAAI,CAAC;IACnB,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/DeckDto.js b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.js new file mode 100644 index 00000000..3a0ee416 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=DeckDto.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/DeckDto.js.map b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.js.map new file mode 100644 index 00000000..cd04d743 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/DeckDto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckDto.js","sourceRoot":"","sources":["../../../src/Application/DTOs/DeckDto.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.d.ts b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.d.ts new file mode 100644 index 00000000..42c677c3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.d.ts @@ -0,0 +1,8 @@ +export declare abstract class BaseMapper { + abstract toShortDto(entity: TEntity): TShortDto; + abstract toDetailDto(entity: TEntity): TDetailDto; + toShortDtoList(entities: TEntity[]): TShortDto[]; + toDetailDtoList(entities: TEntity[]): TDetailDto[]; + static toShortDtoListStatic(entities: T[], mapperFn: (entity: T) => TDto): TDto[]; +} +//# sourceMappingURL=BaseMapper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.d.ts.map new file mode 100644 index 00000000..98170754 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"BaseMapper.d.ts","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/BaseMapper.ts"],"names":[],"mappings":"AAAA,8BAAsB,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU;IAC7D,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS;IAC/C,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU;IAEjD,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE;IAIhD,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE;IAIlD,MAAM,CAAC,oBAAoB,CAAC,CAAC,EAAE,IAAI,EACjC,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,GAC5B,IAAI,EAAE;CAGV"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.js b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.js new file mode 100644 index 00000000..14543c51 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BaseMapper = void 0; +class BaseMapper { + toShortDtoList(entities) { + return entities.map(entity => this.toShortDto(entity)); + } + toDetailDtoList(entities) { + return entities.map(entity => this.toDetailDto(entity)); + } + static toShortDtoListStatic(entities, mapperFn) { + return entities.map(mapperFn); + } +} +exports.BaseMapper = BaseMapper; +//# sourceMappingURL=BaseMapper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.js.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.js.map new file mode 100644 index 00000000..330c2fdb --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/BaseMapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BaseMapper.js","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/BaseMapper.ts"],"names":[],"mappings":";;;AAAA,MAAsB,UAAU;IAI9B,cAAc,CAAC,QAAmB;QAChC,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,eAAe,CAAC,QAAmB;QACjC,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,oBAAoB,CACzB,QAAa,EACb,QAA6B;QAE7B,OAAO,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;CACF;AAlBD,gCAkBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.d.ts b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.d.ts new file mode 100644 index 00000000..970ab773 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.d.ts @@ -0,0 +1,8 @@ +import { ChatAggregate } from '../../../Domain/Chat/ChatAggregate'; +import { ShortChatDto, DetailChatDto } from '../ChatDto'; +export declare class ChatMapper { + static toShortDto(chat: ChatAggregate): ShortChatDto; + static toDetailDto(chat: ChatAggregate): DetailChatDto; + static toShortDtoList(chats: ChatAggregate[]): ShortChatDto[]; +} +//# sourceMappingURL=ChatMapper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.d.ts.map new file mode 100644 index 00000000..13f2ae4f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatMapper.d.ts","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/ChatMapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzD,qBAAa,UAAU;IACrB,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY;IAQpD,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa;IAUtD,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE;CAG9D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.js b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.js new file mode 100644 index 00000000..378ccd6c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatMapper = void 0; +class ChatMapper { + static toShortDto(chat) { + return { + id: chat.id, + userCount: chat.users?.length ?? 0, + state: chat.state, + }; + } + static toDetailDto(chat) { + return { + id: chat.id, + users: chat.users ?? [], + messages: chat.messages, + updateDate: chat.updateDate, + state: chat.state, + }; + } + static toShortDtoList(chats) { + return chats.map(this.toShortDto); + } +} +exports.ChatMapper = ChatMapper; +//# sourceMappingURL=ChatMapper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.js.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.js.map new file mode 100644 index 00000000..c1c773b5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ChatMapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatMapper.js","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/ChatMapper.ts"],"names":[],"mappings":";;;AAGA,MAAa,UAAU;IACrB,MAAM,CAAC,UAAU,CAAC,IAAmB;QACnC,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;YAClC,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,IAAmB;QACpC,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,KAAsB;QAC1C,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;CACF;AAtBD,gCAsBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.d.ts b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.d.ts new file mode 100644 index 00000000..8b4ca8a1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.d.ts @@ -0,0 +1,8 @@ +import { ContactAggregate } from '../../../Domain/Contact/ContactAggregate'; +import { ShortContactDto, DetailContactDto } from '../ContactDto'; +export declare class ContactMapper { + static toShortDto(contact: ContactAggregate): ShortContactDto; + static toDetailDto(contact: ContactAggregate): DetailContactDto; + static toShortDtoList(contacts: ContactAggregate[]): ShortContactDto[]; +} +//# sourceMappingURL=ContactMapper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.d.ts.map new file mode 100644 index 00000000..113bb87c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactMapper.d.ts","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/ContactMapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAsC,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtG,qBAAa,aAAa;IACxB,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe;IAW7D,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB;IAiB/D,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,eAAe,EAAE;CAGvE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.js b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.js new file mode 100644 index 00000000..05e1d115 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContactMapper = void 0; +class ContactMapper { + static toShortDto(contact) { + return { + id: contact.id, + name: contact.name, + email: contact.email, + type: contact.type, + createDate: contact.createDate, + state: contact.state, + }; + } + static toDetailDto(contact) { + return { + id: contact.id, + name: contact.name, + email: contact.email, + userid: contact.userid, + type: contact.type, + txt: contact.txt, + state: contact.state, + createDate: contact.createDate, + updateDate: contact.updateDate, + adminResponse: contact.adminResponse, + responseDate: contact.responseDate, + respondedBy: contact.respondedBy, + }; + } + static toShortDtoList(contacts) { + return contacts.map(this.toShortDto); + } +} +exports.ContactMapper = ContactMapper; +//# sourceMappingURL=ContactMapper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.js.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.js.map new file mode 100644 index 00000000..5c339b2b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/ContactMapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactMapper.js","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/ContactMapper.ts"],"names":[],"mappings":";;;AAGA,MAAa,aAAa;IACxB,MAAM,CAAC,UAAU,CAAC,OAAyB;QACzC,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,OAAyB;QAC1C,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,QAA4B;QAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;CACF;AAhCD,sCAgCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.d.ts b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.d.ts new file mode 100644 index 00000000..65d7c56f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.d.ts @@ -0,0 +1,8 @@ +import { DeckAggregate } from '../../../Domain/Deck/DeckAggregate'; +import { ShortDeckDto, DetailDeckDto } from '../DeckDto'; +export declare class DeckMapper { + static toShortDto(deck: DeckAggregate): ShortDeckDto; + static toDetailDto(deck: DeckAggregate): DetailDeckDto; + static toShortDtoList(decks: DeckAggregate[]): ShortDeckDto[]; +} +//# sourceMappingURL=DeckMapper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.d.ts.map new file mode 100644 index 00000000..a3cd73da --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckMapper.d.ts","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/DeckMapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAgC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEvF,qBAAa,UAAU;IACrB,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY;IAUpD,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa;IAatD,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE;CAG9D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.js b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.js new file mode 100644 index 00000000..3633551c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeckMapper = void 0; +class DeckMapper { + static toShortDto(deck) { + return { + id: deck.id, + name: deck.name, + type: deck.type, + playedNumber: deck.playedNumber, + ctype: deck.ctype, + }; + } + static toDetailDto(deck) { + return { + id: deck.id, + name: deck.name, + type: deck.type, + userid: deck.userid, + creationdate: deck.creationdate, + cards: deck.cards, + playedNumber: deck.playedNumber, + ctype: deck.ctype, + }; + } + static toShortDtoList(decks) { + return decks.map(this.toShortDto); + } +} +exports.DeckMapper = DeckMapper; +//# sourceMappingURL=DeckMapper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.js.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.js.map new file mode 100644 index 00000000..8a6da43c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/DeckMapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckMapper.js","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/DeckMapper.ts"],"names":[],"mappings":";;;AAGA,MAAa,UAAU;IACrB,MAAM,CAAC,UAAU,CAAC,IAAmB;QACnC,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,IAAmB;QACpC,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,KAAsB;QAC1C,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;CACF;AA3BD,gCA2BC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.d.ts b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.d.ts new file mode 100644 index 00000000..4193d2dc --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.d.ts @@ -0,0 +1,8 @@ +import { OrganizationAggregate } from '../../../Domain/Organization/OrganizationAggregate'; +import { ShortOrganizationDto, DetailOrganizationDto } from '../OrganizationDto'; +export declare class OrganizationMapper { + static toShortDto(org: OrganizationAggregate): ShortOrganizationDto; + static toDetailDto(org: OrganizationAggregate): DetailOrganizationDto; + static toShortDtoList(orgs: OrganizationAggregate[]): ShortOrganizationDto[]; +} +//# sourceMappingURL=OrganizationMapper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.d.ts.map new file mode 100644 index 00000000..2e4ad5c7 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationMapper.d.ts","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/OrganizationMapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oDAAoD,CAAC;AAC3F,OAAO,EAAgD,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE/H,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,qBAAqB,GAAG,oBAAoB;IAUnE,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,qBAAqB,GAAG,qBAAqB;IAkBrE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,EAAE,GAAG,oBAAoB,EAAE;CAG7E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.js b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.js new file mode 100644 index 00000000..c24c7f66 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OrganizationMapper = void 0; +class OrganizationMapper { + static toShortDto(org) { + return { + id: org.id, + name: org.name, + state: org.state, + userinorg: org.userinorg, + maxOrganizationalDecks: org.maxOrganizationalDecks, + }; + } + static toDetailDto(org) { + return { + id: org.id, + name: org.name, + contactfname: org.contactfname, + contactlname: org.contactlname, + contactphone: org.contactphone, + contactemail: org.contactemail, + state: org.state, + regdate: org.regdate, + updatedate: org.updatedate, + url: org.url, + userinorg: org.userinorg, + maxOrganizationalDecks: org.maxOrganizationalDecks, + users: org.users?.map(u => u.id) ?? [], + }; + } + static toShortDtoList(orgs) { + return orgs.map(this.toShortDto); + } +} +exports.OrganizationMapper = OrganizationMapper; +//# sourceMappingURL=OrganizationMapper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.js.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.js.map new file mode 100644 index 00000000..b20d58cf --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/OrganizationMapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationMapper.js","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/OrganizationMapper.ts"],"names":[],"mappings":";;;AAGA,MAAa,kBAAkB;IAC7B,MAAM,CAAC,UAAU,CAAC,GAA0B;QAC1C,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,sBAAsB,EAAE,GAAG,CAAC,sBAAsB;SACnD,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,GAA0B;QAC3C,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,sBAAsB,EAAE,GAAG,CAAC,sBAAsB;YAClD,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE;SACvC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,IAA6B;QACjD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;CACF;AAhCD,gDAgCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.d.ts b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.d.ts new file mode 100644 index 00000000..b4602706 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.d.ts @@ -0,0 +1,8 @@ +import { UserAggregate } from '../../../Domain/User/UserAggregate'; +import { ShortUserDto, DetailUserDto } from '../UserDto'; +export declare class UserMapper { + static toShortDto(user: UserAggregate): ShortUserDto; + static toDetailDto(user: UserAggregate): DetailUserDto; + static toShortDtoList(users: UserAggregate[]): ShortUserDto[]; +} +//# sourceMappingURL=UserMapper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.d.ts.map new file mode 100644 index 00000000..ee4b1b0d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UserMapper.d.ts","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/UserMapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAa,MAAM,oCAAoC,CAAC;AAC9E,OAAO,EAAgC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGvF,qBAAa,UAAU;IACrB,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY;IASpD,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa;IAetD,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE;CAG9D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.js b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.js new file mode 100644 index 00000000..d1f8d283 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserMapper = void 0; +const UserAggregate_1 = require("../../../Domain/User/UserAggregate"); +const BaseMapper_1 = require("./BaseMapper"); +class UserMapper { + static toShortDto(user) { + return { + id: user.id, + username: user.username, + state: user.state, + authLevel: (user.state === UserAggregate_1.UserState.ADMIN ? 1 : 0), + }; + } + static toDetailDto(user) { + return { + id: user.id, + orgid: user.orgid, + username: user.username, + email: user.email, + fname: user.fname, + lname: user.lname, + code: user.token, + type: user.type, + phone: user.phone, + state: user.state, + }; + } + static toShortDtoList(users) { + return BaseMapper_1.BaseMapper.toShortDtoListStatic(users, UserMapper.toShortDto); + } +} +exports.UserMapper = UserMapper; +//# sourceMappingURL=UserMapper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.js.map b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.js.map new file mode 100644 index 00000000..1d67ea9c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/Mappers/UserMapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UserMapper.js","sourceRoot":"","sources":["../../../../src/Application/DTOs/Mappers/UserMapper.ts"],"names":[],"mappings":";;;AAAA,sEAA8E;AAE9E,6CAA0C;AAE1C,MAAa,UAAU;IACrB,MAAM,CAAC,UAAU,CAAC,IAAmB;QACnC,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,yBAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAU;SAC7D,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,IAAmB;QACpC,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,KAAK;YAChB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,KAAsB;QAC1C,OAAO,uBAAU,CAAC,oBAAoB,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;IACvE,CAAC;CACF;AA5BD,gCA4BC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.d.ts b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.d.ts new file mode 100644 index 00000000..57d8b338 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.d.ts @@ -0,0 +1,44 @@ +export interface CreateOrganizationDto { + name: string; + description?: string; + maxOrganizationalDecks?: number | null; +} +export interface UpdateOrganizationDto { + id: string; + name?: string; + description?: string; +} +export interface ShortOrganizationDto { + id: string; + name: string; + state: number; + userinorg: number; + maxOrganizationalDecks?: number | null; +} +export interface DetailOrganizationDto { + id: string; + name: string; + contactfname: string; + contactlname: string; + contactphone: string; + contactemail: string; + state: number; + regdate: Date; + updatedate: Date; + url: string | null; + userinorg: number; + maxOrganizationalDecks: number | null; + users: string[]; +} +export interface OrganizationLoginUrlDto { + organizationId: string; + organizationName: string; + loginUrl: string; +} +export interface OrganizationAuthCallbackDto { + organizationId: string; + userId: string; + status: 'ok' | 'not_ok'; + authToken?: string; +} +//# sourceMappingURL=OrganizationDto.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.d.ts.map new file mode 100644 index 00000000..7235c3b4 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationDto.d.ts","sourceRoot":"","sources":["../../../src/Application/DTOs/OrganizationDto.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,IAAI,CAAC;IACd,UAAU,EAAE,IAAI,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.js b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.js new file mode 100644 index 00000000..4bd33d30 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=OrganizationDto.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.js.map b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.js.map new file mode 100644 index 00000000..afae12cb --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/OrganizationDto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationDto.js","sourceRoot":"","sources":["../../../src/Application/DTOs/OrganizationDto.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/SearchDto.d.ts b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.d.ts new file mode 100644 index 00000000..08c8ff9c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.d.ts @@ -0,0 +1,13 @@ +export interface SearchQuery { + query: string; + limit?: number; + offset?: number; +} +export interface SearchResult { + results: T[]; + totalCount: number; + hasMore: boolean; + searchQuery: string; + searchType: 'users' | 'organizations' | 'decks'; +} +//# sourceMappingURL=SearchDto.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/SearchDto.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.d.ts.map new file mode 100644 index 00000000..f8ff6c1e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SearchDto.d.ts","sourceRoot":"","sources":["../../../src/Application/DTOs/SearchDto.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,GAAG,eAAe,GAAG,OAAO,CAAC;CACjD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/SearchDto.js b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.js new file mode 100644 index 00000000..3902d011 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=SearchDto.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/SearchDto.js.map b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.js.map new file mode 100644 index 00000000..807effdd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/SearchDto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SearchDto.js","sourceRoot":"","sources":["../../../src/Application/DTOs/SearchDto.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/UserDto.d.ts b/SerpentRace_Backend/dist/Application/DTOs/UserDto.d.ts new file mode 100644 index 00000000..27d86ac1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/UserDto.d.ts @@ -0,0 +1,28 @@ +export interface CreateUserDto { + username: string; + email: string; +} +export interface UpdateUserDto { + id: string; + username?: string; + email?: string; +} +export interface ShortUserDto { + id: string; + username: string; + state: number; + authLevel: 0 | 1; +} +export interface DetailUserDto { + id: string; + orgid: string | null; + username: string; + email: string; + fname: string; + lname: string; + code: string | null; + type: string; + phone: string | null; + state: number; +} +//# sourceMappingURL=UserDto.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/UserDto.d.ts.map b/SerpentRace_Backend/dist/Application/DTOs/UserDto.d.ts.map new file mode 100644 index 00000000..ae72cfe8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/UserDto.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UserDto.d.ts","sourceRoot":"","sources":["../../../src/Application/DTOs/UserDto.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/UserDto.js b/SerpentRace_Backend/dist/Application/DTOs/UserDto.js new file mode 100644 index 00000000..4c23cc90 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/UserDto.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=UserDto.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/DTOs/UserDto.js.map b/SerpentRace_Backend/dist/Application/DTOs/UserDto.js.map new file mode 100644 index 00000000..344121b3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/DTOs/UserDto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UserDto.js","sourceRoot":"","sources":["../../../src/Application/DTOs/UserDto.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.d.ts b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.d.ts new file mode 100644 index 00000000..a90370e9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.d.ts @@ -0,0 +1,8 @@ +export interface CreateDeckCommand { + name: string; + type: number; + userid: string; + cards: any[]; + ctype?: number; +} +//# sourceMappingURL=CreateDeckCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.d.ts.map new file mode 100644 index 00000000..7e472d3c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateDeckCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/CreateDeckCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.js b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.js new file mode 100644 index 00000000..131d4b5a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=CreateDeckCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.js.map b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.js.map new file mode 100644 index 00000000..a6acca1e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateDeckCommand.js","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/CreateDeckCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.d.ts new file mode 100644 index 00000000..509c30fb --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.d.ts @@ -0,0 +1,17 @@ +import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { CreateDeckCommand } from './CreateDeckCommand'; +import { ShortDeckDto } from '../../DTOs/DeckDto'; +export declare class CreateDeckCommandHandler { + private readonly deckRepo; + private readonly userRepo; + private readonly orgRepo; + constructor(deckRepo: IDeckRepository, userRepo: IUserRepository, orgRepo: IOrganizationRepository); + execute(cmd: CreateDeckCommand): Promise; + /** + * Private method to create deck after all validations + */ + private createDeck; +} +//# sourceMappingURL=CreateDeckCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.d.ts.map new file mode 100644 index 00000000..ebf5896d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateDeckCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/CreateDeckCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOlD,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,uBAAuB;IAG7C,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IAsE5D;;OAEG;YACW,UAAU;CAiCzB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.js b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.js new file mode 100644 index 00000000..099f1874 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.js @@ -0,0 +1,105 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateDeckCommandHandler = void 0; +const DeckAggregate_1 = require("../../../Domain/Deck/DeckAggregate"); +const UserAggregate_1 = require("../../../Domain/User/UserAggregate"); +const DeckMapper_1 = require("../../DTOs/Mappers/DeckMapper"); +const AdminBypassService_1 = require("../../Services/AdminBypassService"); +const Logger_1 = require("../../Services/Logger"); +class CreateDeckCommandHandler { + constructor(deckRepo, userRepo, orgRepo) { + this.deckRepo = deckRepo; + this.userRepo = userRepo; + this.orgRepo = orgRepo; + } + async execute(cmd) { + try { + // 1. Get user details + const user = await this.userRepo.findById(cmd.userid); + if (!user) { + throw new Error('User not found'); + } + // 2. ADMIN BYPASS - Skip all restrictions + if (AdminBypassService_1.AdminBypassService.shouldBypassRestrictions(user.state)) { + AdminBypassService_1.AdminBypassService.logAdminBypass('CREATE_DECK_BYPASS', user.id, 'new-deck', { + deckName: cmd.name, + deckType: cmd.type, + cardCount: cmd.cards.length, + ctype: cmd.ctype + }); + return this.createDeck(cmd); + } + // 3. Check deck count limits for regular users + const userDeckCount = await this.deckRepo.countActiveByUserId(cmd.userid); + const maxDecks = user.state === UserAggregate_1.UserState.VERIFIED_PREMIUM ? 12 : 8; + if (userDeckCount >= maxDecks) { + throw new Error(`Deck limit exceeded. Maximum ${maxDecks} decks allowed for your account type.`); + } + // 4. Organizational deck restrictions + if (cmd.ctype === DeckAggregate_1.CType.ORGANIZATION) { + // Only premium users can create organizational decks + if (user.state !== UserAggregate_1.UserState.VERIFIED_PREMIUM) { + throw new Error('Only premium users can create organizational decks.'); + } + // User must belong to an organization + if (!user.orgid) { + throw new Error('You must be a member of an organization to create organizational decks.'); + } + // Check organization limits + const org = await this.orgRepo.findById(user.orgid); + if (!org) { + throw new Error('Organization not found.'); + } + if (org.maxOrganizationalDecks === null) { + throw new Error('Organization deck limit not configured. Contact administrator.'); + } + const userOrgDeckCount = await this.deckRepo.countOrganizationalByUserId(cmd.userid); + if (userOrgDeckCount >= org.maxOrganizationalDecks) { + throw new Error(`Organization deck limit exceeded. Maximum ${org.maxOrganizationalDecks} organizational decks allowed.`); + } + } + // 5. Create deck with restrictions passed + return this.createDeck(cmd); + } + catch (error) { + if (error instanceof Error) { + throw error; // Re-throw known errors with original message + } + throw new Error('Failed to create deck'); + } + } + /** + * Private method to create deck after all validations + */ + async createDeck(cmd) { + const deck = new DeckAggregate_1.DeckAggregate(); + deck.name = cmd.name; + deck.type = cmd.type; + deck.userid = cmd.userid; + deck.cards = cmd.cards; + deck.ctype = cmd.ctype ?? DeckAggregate_1.CType.PUBLIC; + deck.state = DeckAggregate_1.State.ACTIVE; + // Set organization reference for organizational decks + if (cmd.ctype === DeckAggregate_1.CType.ORGANIZATION) { + const user = await this.userRepo.findById(cmd.userid); + if (user?.orgid) { + const org = await this.orgRepo.findById(user.orgid); + if (org) { + deck.organization = org; + } + } + } + const created = await this.deckRepo.create(deck); + (0, Logger_1.logRequest)('Deck created successfully', undefined, undefined, { + deckId: created.id, + userId: cmd.userid, + deckName: cmd.name, + deckType: cmd.type, + ctype: cmd.ctype, + cardCount: cmd.cards.length + }); + return DeckMapper_1.DeckMapper.toShortDto(created); + } +} +exports.CreateDeckCommandHandler = CreateDeckCommandHandler; +//# sourceMappingURL=CreateDeckCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.js.map new file mode 100644 index 00000000..673b4fd9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/CreateDeckCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateDeckCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/CreateDeckCommandHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.d.ts b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.d.ts new file mode 100644 index 00000000..8ca63383 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.d.ts @@ -0,0 +1,5 @@ +export interface DeleteDeckCommand { + id: string; + soft?: boolean; +} +//# sourceMappingURL=DeleteDeckCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.d.ts.map new file mode 100644 index 00000000..1e21ce9a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteDeckCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/DeleteDeckCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.js b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.js new file mode 100644 index 00000000..3c66e377 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=DeleteDeckCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.js.map b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.js.map new file mode 100644 index 00000000..24de4184 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteDeckCommand.js","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/DeleteDeckCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.d.ts new file mode 100644 index 00000000..b1e18407 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; +import { DeleteDeckCommand } from './DeleteDeckCommand'; +export declare class DeleteDeckCommandHandler { + private readonly deckRepo; + constructor(deckRepo: IDeckRepository); + execute(cmd: DeleteDeckCommand): Promise; +} +//# sourceMappingURL=DeleteDeckCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.d.ts.map new file mode 100644 index 00000000..ef6e6c0a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteDeckCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/DeleteDeckCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;CAQxD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.js b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.js new file mode 100644 index 00000000..f6ea3302 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteDeckCommandHandler = void 0; +class DeleteDeckCommandHandler { + constructor(deckRepo) { + this.deckRepo = deckRepo; + } + async execute(cmd) { + if (cmd.soft) { + await this.deckRepo.softDelete(cmd.id); + } + else { + await this.deckRepo.delete(cmd.id); + } + return true; + } +} +exports.DeleteDeckCommandHandler = DeleteDeckCommandHandler; +//# sourceMappingURL=DeleteDeckCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.js.map new file mode 100644 index 00000000..102a9d7a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/DeleteDeckCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteDeckCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/DeleteDeckCommandHandler.ts"],"names":[],"mappings":";;;AAGA,MAAa,wBAAwB;IACnC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,GAAsB;QAClC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAXD,4DAWC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.d.ts b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.d.ts new file mode 100644 index 00000000..300f84e9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.d.ts @@ -0,0 +1,10 @@ +export interface UpdateDeckCommand { + id: string; + name?: string; + type?: number; + userid?: string; + cards?: any[]; + ctype?: number; + state?: number; +} +//# sourceMappingURL=UpdateDeckCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.d.ts.map new file mode 100644 index 00000000..02c74a33 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateDeckCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/UpdateDeckCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.js b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.js new file mode 100644 index 00000000..f33c190b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=UpdateDeckCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.js.map b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.js.map new file mode 100644 index 00000000..f3c74623 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateDeckCommand.js","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/UpdateDeckCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.d.ts new file mode 100644 index 00000000..61035abc --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; +import { UpdateDeckCommand } from './UpdateDeckCommand'; +import { ShortDeckDto } from '../../DTOs/DeckDto'; +export declare class UpdateDeckCommandHandler { + private readonly deckRepo; + constructor(deckRepo: IDeckRepository); + execute(cmd: UpdateDeckCommand): Promise; +} +//# sourceMappingURL=UpdateDeckCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.d.ts.map new file mode 100644 index 00000000..ee28a52f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateDeckCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/UpdateDeckCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAKpE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.js b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.js new file mode 100644 index 00000000..e21db006 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateDeckCommandHandler = void 0; +const DeckMapper_1 = require("../../DTOs/Mappers/DeckMapper"); +class UpdateDeckCommandHandler { + constructor(deckRepo) { + this.deckRepo = deckRepo; + } + async execute(cmd) { + const updated = await this.deckRepo.update(cmd.id, { ...cmd }); + if (!updated) + return null; + return DeckMapper_1.DeckMapper.toShortDto(updated); + } +} +exports.UpdateDeckCommandHandler = UpdateDeckCommandHandler; +//# sourceMappingURL=UpdateDeckCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.js.map new file mode 100644 index 00000000..5bf59127 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/commands/UpdateDeckCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateDeckCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Deck/commands/UpdateDeckCommandHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAE3D,MAAa,wBAAwB;IACnC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,GAAsB;QAClC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,OAAO,uBAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;CACF;AARD,4DAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.d.ts b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.d.ts new file mode 100644 index 00000000..f7056562 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.d.ts @@ -0,0 +1,4 @@ +export interface GetDeckByIdQuery { + id: string; +} +//# sourceMappingURL=GetDeckByIdQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.d.ts.map new file mode 100644 index 00000000..9db7c375 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDeckByIdQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDeckByIdQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.js b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.js new file mode 100644 index 00000000..62eeb764 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetDeckByIdQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.js.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.js.map new file mode 100644 index 00000000..f90c42cd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDeckByIdQuery.js","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDeckByIdQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.d.ts new file mode 100644 index 00000000..94770f06 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.d.ts @@ -0,0 +1,9 @@ +import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; +import { GetDeckByIdQuery } from './GetDeckByIdQuery'; +import { ShortDeckDto } from '../../DTOs/DeckDto'; +export declare class GetDeckByIdQueryHandler { + private readonly deckRepo; + constructor(deckRepo: IDeckRepository); + execute(query: GetDeckByIdQuery): Promise; +} +//# sourceMappingURL=GetDeckByIdQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.d.ts.map new file mode 100644 index 00000000..059a9036 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDeckByIdQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDeckByIdQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAKrE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.js b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.js new file mode 100644 index 00000000..9e74e39b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetDeckByIdQueryHandler = void 0; +const DeckMapper_1 = require("../../DTOs/Mappers/DeckMapper"); +class GetDeckByIdQueryHandler { + constructor(deckRepo) { + this.deckRepo = deckRepo; + } + async execute(query) { + const deck = await this.deckRepo.findById(query.id); + if (!deck) + return null; + return DeckMapper_1.DeckMapper.toShortDto(deck); + } +} +exports.GetDeckByIdQueryHandler = GetDeckByIdQueryHandler; +//# sourceMappingURL=GetDeckByIdQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.js.map new file mode 100644 index 00000000..dced6929 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDeckByIdQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDeckByIdQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDeckByIdQueryHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAE3D,MAAa,uBAAuB;IAClC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,KAAuB;QACnC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO,uBAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;CACF;AARD,0DAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.d.ts b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.d.ts new file mode 100644 index 00000000..cbcb61f6 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.d.ts @@ -0,0 +1,9 @@ +export interface GetDecksByPageQuery { + from: number; + to: number; + userId: string; + userOrgId?: string; + isAdmin: boolean; + includeDeleted?: boolean; +} +//# sourceMappingURL=GetDecksByPageQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.d.ts.map new file mode 100644 index 00000000..69d31b46 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDecksByPageQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDecksByPageQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.js b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.js new file mode 100644 index 00000000..73a8cd73 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetDecksByPageQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.js.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.js.map new file mode 100644 index 00000000..20e0bc8c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDecksByPageQuery.js","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDecksByPageQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.d.ts new file mode 100644 index 00000000..c7225ba0 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.d.ts @@ -0,0 +1,12 @@ +import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; +import { GetDecksByPageQuery } from './GetDecksByPageQuery'; +import { ShortDeckDto } from '../../DTOs/DeckDto'; +export declare class GetDecksByPageQueryHandler { + private readonly deckRepo; + constructor(deckRepo: IDeckRepository); + execute(query: GetDecksByPageQuery): Promise<{ + decks: ShortDeckDto[]; + totalCount: number; + }>; +} +//# sourceMappingURL=GetDecksByPageQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.d.ts.map new file mode 100644 index 00000000..0c004871 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDecksByPageQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDecksByPageQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKlD,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAuElG"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.js b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.js new file mode 100644 index 00000000..b4039f62 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetDecksByPageQueryHandler = void 0; +const DeckMapper_1 = require("../../DTOs/Mappers/DeckMapper"); +const AdminBypassService_1 = require("../../Services/AdminBypassService"); +const Logger_1 = require("../../Services/Logger"); +class GetDecksByPageQueryHandler { + constructor(deckRepo) { + this.deckRepo = deckRepo; + } + async execute(query) { + try { + // Validate pagination parameters + if (query.from < 0 || query.to < query.from) { + throw new Error('Invalid pagination parameters'); + } + const limit = query.to - query.from + 1; + if (limit > 100) { + throw new Error('Page size too large. Maximum 100 records per request'); + } + // Log admin bypass if applicable + if (query.isAdmin) { + AdminBypassService_1.AdminBypassService.logAdminBypass('GET_DECKS_PAGE_BYPASS', query.userId, 'paginated-decks', { + from: query.from, + to: query.to, + includesDeleted: query.includeDeleted || false, + operation: 'read' + }); + } + (0, Logger_1.logRequest)('Get decks by page query started', undefined, undefined, { + userId: query.userId, + userOrgId: query.userOrgId, + isAdmin: query.isAdmin, + from: query.from, + to: query.to, + includeDeleted: query.includeDeleted || false + }); + // Use paginated filtered deck finding method + const result = await this.deckRepo.findFilteredDecks(query.userId, query.userOrgId, query.isAdmin, query.from, query.to); + (0, Logger_1.logRequest)('Get decks by page query completed', undefined, undefined, { + userId: query.userId, + userOrgId: query.userOrgId, + isAdmin: query.isAdmin, + from: query.from, + to: query.to, + returned: result.decks.length, + totalCount: result.totalCount, + includeDeleted: query.includeDeleted || false + }); + return { + decks: DeckMapper_1.DeckMapper.toShortDtoList(result.decks), + totalCount: result.totalCount + }; + } + catch (error) { + (0, Logger_1.logError)('GetDecksByPageQueryHandler error', error instanceof Error ? error : new Error(String(error))); + // Re-throw validation errors as-is + if (error instanceof Error && (error.message.includes('Invalid pagination') || error.message.includes('Page size'))) { + throw error; + } + throw new Error('Failed to retrieve decks page'); + } + } +} +exports.GetDecksByPageQueryHandler = GetDecksByPageQueryHandler; +//# sourceMappingURL=GetDecksByPageQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.js.map new file mode 100644 index 00000000..0f039d93 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Deck/queries/GetDecksByPageQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetDecksByPageQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Deck/queries/GetDecksByPageQueryHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.d.ts new file mode 100644 index 00000000..368d0110 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.d.ts @@ -0,0 +1,9 @@ +export interface CreateOrganizationCommand { + name: string; + contactfname: string; + contactlname: string; + contactphone: string; + contactemail: string; + url?: string; +} +//# sourceMappingURL=CreateOrganizationCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.d.ts.map new file mode 100644 index 00000000..9dd4d856 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateOrganizationCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/CreateOrganizationCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.js b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.js new file mode 100644 index 00000000..a57a5cbd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=CreateOrganizationCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.js.map new file mode 100644 index 00000000..45fa4096 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateOrganizationCommand.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/CreateOrganizationCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.d.ts new file mode 100644 index 00000000..118ca280 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { CreateOrganizationCommand } from './CreateOrganizationCommand'; +import { ShortOrganizationDto } from '../../DTOs/OrganizationDto'; +export declare class CreateOrganizationCommandHandler { + private readonly orgRepo; + constructor(orgRepo: IOrganizationRepository); + execute(cmd: CreateOrganizationCommand): Promise; +} +//# sourceMappingURL=CreateOrganizationCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.d.ts.map new file mode 100644 index 00000000..85852b35 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateOrganizationCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/CreateOrganizationCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAIlE,qBAAa,gCAAgC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,OAAO,CAAC,GAAG,EAAE,yBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAsB7E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.js b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.js new file mode 100644 index 00000000..215a97ec --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateOrganizationCommandHandler = void 0; +const OrganizationAggregate_1 = require("../../../Domain/Organization/OrganizationAggregate"); +const OrganizationMapper_1 = require("../../DTOs/Mappers/OrganizationMapper"); +class CreateOrganizationCommandHandler { + constructor(orgRepo) { + this.orgRepo = orgRepo; + } + async execute(cmd) { + try { + const org = new OrganizationAggregate_1.OrganizationAggregate(); + org.name = cmd.name; + org.contactfname = cmd.contactfname; + org.contactlname = cmd.contactlname; + org.contactphone = cmd.contactphone; + org.contactemail = cmd.contactemail; + org.url = cmd.url || null; + org.state = OrganizationAggregate_1.OrganizationState.REGISTERED; + const created = await this.orgRepo.create(org); + return OrganizationMapper_1.OrganizationMapper.toShortDto(created); + } + catch (error) { + if (error instanceof Error) { + if (error.message.includes('duplicate key value violates unique constraint')) { + throw new Error('Organization with this name or contact email already exists'); + } + } + throw new Error('Failed to create organization'); + } + } +} +exports.CreateOrganizationCommandHandler = CreateOrganizationCommandHandler; +//# sourceMappingURL=CreateOrganizationCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.js.map new file mode 100644 index 00000000..977450e1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/CreateOrganizationCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateOrganizationCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/CreateOrganizationCommandHandler.ts"],"names":[],"mappings":";;;AAGA,8FAA8G;AAC9G,8EAA2E;AAE3E,MAAa,gCAAgC;IAC3C,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,GAA8B;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,6CAAqB,EAAE,CAAC;YACxC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YACpB,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;YACpC,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;YACpC,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;YACpC,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;YACpC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC;YAC1B,GAAG,CAAC,KAAK,GAAG,yCAAiB,CAAC,UAAU,CAAC;YAEzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/C,OAAO,uCAAkB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,gDAAgD,CAAC,EAAE,CAAC;oBAC7E,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;gBACjF,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;CACF;AAzBD,4EAyBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.d.ts new file mode 100644 index 00000000..cf1fa58b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.d.ts @@ -0,0 +1,5 @@ +export interface DeleteOrganizationCommand { + id: string; + soft?: boolean; +} +//# sourceMappingURL=DeleteOrganizationCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.d.ts.map new file mode 100644 index 00000000..4a379951 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteOrganizationCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/DeleteOrganizationCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.js b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.js new file mode 100644 index 00000000..74db6349 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=DeleteOrganizationCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.js.map new file mode 100644 index 00000000..ffbe3cdd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteOrganizationCommand.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/DeleteOrganizationCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.d.ts new file mode 100644 index 00000000..d00679b4 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { DeleteOrganizationCommand } from './DeleteOrganizationCommand'; +export declare class DeleteOrganizationCommandHandler { + private readonly orgRepo; + constructor(orgRepo: IOrganizationRepository); + execute(cmd: DeleteOrganizationCommand): Promise; +} +//# sourceMappingURL=DeleteOrganizationCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.d.ts.map new file mode 100644 index 00000000..23917a20 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteOrganizationCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/DeleteOrganizationCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAGxE,qBAAa,gCAAgC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,OAAO,CAAC,GAAG,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC;CAQhE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.js b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.js new file mode 100644 index 00000000..ba47b855 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteOrganizationCommandHandler = void 0; +class DeleteOrganizationCommandHandler { + constructor(orgRepo) { + this.orgRepo = orgRepo; + } + async execute(cmd) { + if (cmd.soft) { + await this.orgRepo.softDelete(cmd.id); + } + else { + await this.orgRepo.delete(cmd.id); + } + return true; + } +} +exports.DeleteOrganizationCommandHandler = DeleteOrganizationCommandHandler; +//# sourceMappingURL=DeleteOrganizationCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.js.map new file mode 100644 index 00000000..f164edda --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/DeleteOrganizationCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteOrganizationCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/DeleteOrganizationCommandHandler.ts"],"names":[],"mappings":";;;AAIA,MAAa,gCAAgC;IAC3C,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,GAA8B;QAC1C,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAXD,4EAWC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.d.ts new file mode 100644 index 00000000..e63e6b53 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.d.ts @@ -0,0 +1,7 @@ +export interface ProcessOrgAuthCallbackCommand { + organizationId: string; + userId: string; + status: 'ok' | 'not_ok'; + authToken?: string; +} +//# sourceMappingURL=ProcessOrgAuthCallbackCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.d.ts.map new file mode 100644 index 00000000..15b68720 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ProcessOrgAuthCallbackCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/ProcessOrgAuthCallbackCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,6BAA6B;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.js b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.js new file mode 100644 index 00000000..2b2f6f24 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ProcessOrgAuthCallbackCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.js.map new file mode 100644 index 00000000..94396c24 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ProcessOrgAuthCallbackCommand.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/ProcessOrgAuthCallbackCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.d.ts new file mode 100644 index 00000000..76cbb7e9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.d.ts @@ -0,0 +1,15 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { ProcessOrgAuthCallbackCommand } from './ProcessOrgAuthCallbackCommand'; +export interface ProcessOrgAuthCallbackResponse { + success: boolean; + message: string; + updatedFields?: string[]; +} +export declare class ProcessOrgAuthCallbackCommandHandler { + private readonly userRepo; + private readonly orgRepo; + constructor(userRepo: IUserRepository, orgRepo: IOrganizationRepository); + execute(cmd: ProcessOrgAuthCallbackCommand): Promise; +} +//# sourceMappingURL=ProcessOrgAuthCallbackCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.d.ts.map new file mode 100644 index 00000000..207539a9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ProcessOrgAuthCallbackCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAGhF,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,qBAAa,oCAAoC;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,uBAAuB;IAG7C,OAAO,CAAC,GAAG,EAAE,6BAA6B,GAAG,OAAO,CAAC,8BAA8B,CAAC;CAyG3F"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.js b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.js new file mode 100644 index 00000000..e7a6bdaf --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.js @@ -0,0 +1,103 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ProcessOrgAuthCallbackCommandHandler = void 0; +const Logger_1 = require("../../Services/Logger"); +class ProcessOrgAuthCallbackCommandHandler { + constructor(userRepo, orgRepo) { + this.userRepo = userRepo; + this.orgRepo = orgRepo; + } + async execute(cmd) { + const startTime = Date.now(); + try { + (0, Logger_1.logAuth)('Processing organization authentication callback', cmd.userId, { + organizationId: cmd.organizationId, + status: cmd.status, + hasAuthToken: !!cmd.authToken + }); + // Verify organization exists + const organization = await this.orgRepo.findById(cmd.organizationId); + if (!organization) { + (0, Logger_1.logWarning)('Organization not found for auth callback', { + organizationId: cmd.organizationId, + userId: cmd.userId + }); + return { + success: false, + message: 'Organization not found' + }; + } + // Verify user exists + const user = await this.userRepo.findById(cmd.userId); + if (!user) { + (0, Logger_1.logWarning)('User not found for auth callback', { + organizationId: cmd.organizationId, + userId: cmd.userId + }); + return { + success: false, + message: 'User not found' + }; + } + // Verify user belongs to the organization + if (user.orgid !== cmd.organizationId) { + (0, Logger_1.logWarning)('User does not belong to organization for auth callback', { + organizationId: cmd.organizationId, + userId: cmd.userId, + userOrgId: user.orgid + }); + return { + success: false, + message: 'User does not belong to this organization' + }; + } + if (cmd.status === 'not_ok') { + (0, Logger_1.logAuth)('Organization authentication failed', cmd.userId, { + organizationId: cmd.organizationId, + organizationName: organization.name + }); + return { + success: false, + message: 'Organization authentication failed' + }; + } + // Update user's organization login date + const now = new Date(); + const updatedUser = await this.userRepo.update(cmd.userId, { + Orglogindate: now + }); + if (!updatedUser) { + (0, Logger_1.logError)('Failed to update user organization login date', new Error('User update returned null')); + return { + success: false, + message: 'Failed to update user login information' + }; + } + (0, Logger_1.logAuth)('Organization authentication successful', cmd.userId, { + organizationId: cmd.organizationId, + organizationName: organization.name, + orgLoginDate: now.toISOString(), + executionTime: Date.now() - startTime + }); + (0, Logger_1.logDatabase)('User organization login date updated', `userId: ${cmd.userId}, orgId: ${cmd.organizationId}`, Date.now() - startTime, { + userId: cmd.userId, + organizationId: cmd.organizationId, + newOrgLoginDate: now.toISOString() + }); + return { + success: true, + message: 'Organization authentication successful', + updatedFields: ['Orglogindate'] + }; + } + catch (error) { + (0, Logger_1.logError)('ProcessOrgAuthCallbackCommandHandler error', error); + return { + success: false, + message: 'Internal error processing authentication callback' + }; + } + } +} +exports.ProcessOrgAuthCallbackCommandHandler = ProcessOrgAuthCallbackCommandHandler; +//# sourceMappingURL=ProcessOrgAuthCallbackCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.js.map new file mode 100644 index 00000000..32bfa2f3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ProcessOrgAuthCallbackCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/ProcessOrgAuthCallbackCommandHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.d.ts new file mode 100644 index 00000000..c18d35f2 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.d.ts @@ -0,0 +1,14 @@ +import { OrganizationStateType } from '../../../Domain/Organization/OrganizationAggregate'; +export interface UpdateOrganizationCommand { + id: string; + name?: string; + contactfname?: string; + contactlname?: string; + contactphone?: string; + contactemail?: string; + url?: string; + state?: OrganizationStateType; + userinorg?: number; + maxOrganizationalDecks?: number | null; +} +//# sourceMappingURL=UpdateOrganizationCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.d.ts.map new file mode 100644 index 00000000..335be16a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateOrganizationCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/UpdateOrganizationCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oDAAoD,CAAC;AAE3F,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.js b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.js new file mode 100644 index 00000000..020d40c7 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=UpdateOrganizationCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.js.map new file mode 100644 index 00000000..2c9b9cd5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateOrganizationCommand.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/UpdateOrganizationCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.d.ts new file mode 100644 index 00000000..c6186e23 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { UpdateOrganizationCommand } from './UpdateOrganizationCommand'; +import { ShortOrganizationDto } from '../../DTOs/OrganizationDto'; +export declare class UpdateOrganizationCommandHandler { + private readonly orgRepo; + constructor(orgRepo: IOrganizationRepository); + execute(cmd: UpdateOrganizationCommand): Promise; +} +//# sourceMappingURL=UpdateOrganizationCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.d.ts.map new file mode 100644 index 00000000..87f2c042 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateOrganizationCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/UpdateOrganizationCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAExE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGlE,qBAAa,gCAAgC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,OAAO,CAAC,GAAG,EAAE,yBAAyB,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;CAKpF"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.js b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.js new file mode 100644 index 00000000..7b6b9ce8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateOrganizationCommandHandler = void 0; +const OrganizationMapper_1 = require("../../DTOs/Mappers/OrganizationMapper"); +class UpdateOrganizationCommandHandler { + constructor(orgRepo) { + this.orgRepo = orgRepo; + } + async execute(cmd) { + const updated = await this.orgRepo.update(cmd.id, { ...cmd }); + if (!updated) + return null; + return OrganizationMapper_1.OrganizationMapper.toShortDto(updated); + } +} +exports.UpdateOrganizationCommandHandler = UpdateOrganizationCommandHandler; +//# sourceMappingURL=UpdateOrganizationCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.js.map new file mode 100644 index 00000000..5484c8d5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/commands/UpdateOrganizationCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateOrganizationCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/commands/UpdateOrganizationCommandHandler.ts"],"names":[],"mappings":";;;AAIA,8EAA2E;AAE3E,MAAa,gCAAgC;IAC3C,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,GAA8B;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,OAAO,uCAAkB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;CACF;AARD,4EAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.d.ts b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.d.ts new file mode 100644 index 00000000..12ac6df5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.d.ts @@ -0,0 +1,4 @@ +export interface GetOrganizationByIdQuery { + id: string; +} +//# sourceMappingURL=GetOrganizationByIdQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.d.ts.map new file mode 100644 index 00000000..9afe1387 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationByIdQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationByIdQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.js b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.js new file mode 100644 index 00000000..f79220b5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetOrganizationByIdQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.js.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.js.map new file mode 100644 index 00000000..52e36dba --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationByIdQuery.js","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationByIdQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.d.ts new file mode 100644 index 00000000..09d73c4e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.d.ts @@ -0,0 +1,9 @@ +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { GetOrganizationByIdQuery } from './GetOrganizationByIdQuery'; +import { ShortOrganizationDto } from '../../DTOs/OrganizationDto'; +export declare class GetOrganizationByIdQueryHandler { + private readonly orgRepo; + constructor(orgRepo: IOrganizationRepository); + execute(query: GetOrganizationByIdQuery): Promise; +} +//# sourceMappingURL=GetOrganizationByIdQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.d.ts.map new file mode 100644 index 00000000..457f6a76 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationByIdQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationByIdQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGlE,qBAAa,+BAA+B;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,OAAO,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;CAKrF"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.js b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.js new file mode 100644 index 00000000..52351000 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetOrganizationByIdQueryHandler = void 0; +const OrganizationMapper_1 = require("../../DTOs/Mappers/OrganizationMapper"); +class GetOrganizationByIdQueryHandler { + constructor(orgRepo) { + this.orgRepo = orgRepo; + } + async execute(query) { + const org = await this.orgRepo.findById(query.id); + if (!org) + return null; + return OrganizationMapper_1.OrganizationMapper.toShortDto(org); + } +} +exports.GetOrganizationByIdQueryHandler = GetOrganizationByIdQueryHandler; +//# sourceMappingURL=GetOrganizationByIdQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.js.map new file mode 100644 index 00000000..b6903fb3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationByIdQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationByIdQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationByIdQueryHandler.ts"],"names":[],"mappings":";;;AAIA,8EAA2E;AAE3E,MAAa,+BAA+B;IAC1C,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,KAA+B;QAC3C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO,uCAAkB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC;CACF;AARD,0EAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.d.ts b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.d.ts new file mode 100644 index 00000000..33cacf49 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.d.ts @@ -0,0 +1,4 @@ +export interface GetOrganizationLoginUrlQuery { + organizationId: string; +} +//# sourceMappingURL=GetOrganizationLoginUrlQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.d.ts.map new file mode 100644 index 00000000..b2debe96 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationLoginUrlQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationLoginUrlQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,MAAM,CAAC;CACxB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.js b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.js new file mode 100644 index 00000000..f67fd300 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetOrganizationLoginUrlQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.js.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.js.map new file mode 100644 index 00000000..21c70a93 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationLoginUrlQuery.js","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationLoginUrlQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.d.ts new file mode 100644 index 00000000..0f568894 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.d.ts @@ -0,0 +1,9 @@ +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { GetOrganizationLoginUrlQuery } from './GetOrganizationLoginUrlQuery'; +import { OrganizationLoginUrlDto } from '../../DTOs/OrganizationDto'; +export declare class GetOrganizationLoginUrlQueryHandler { + private readonly orgRepo; + constructor(orgRepo: IOrganizationRepository); + execute(query: GetOrganizationLoginUrlQuery): Promise; +} +//# sourceMappingURL=GetOrganizationLoginUrlQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.d.ts.map new file mode 100644 index 00000000..552684ca --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationLoginUrlQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,qBAAa,mCAAmC;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,OAAO,CAAC,KAAK,EAAE,4BAA4B,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;CA+C5F"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.js b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.js new file mode 100644 index 00000000..95e4fab3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetOrganizationLoginUrlQueryHandler = void 0; +const Logger_1 = require("../../Services/Logger"); +class GetOrganizationLoginUrlQueryHandler { + constructor(orgRepo) { + this.orgRepo = orgRepo; + } + async execute(query) { + const startTime = Date.now(); + try { + (0, Logger_1.logDatabase)('Getting organization login URL', `organizationId: ${query.organizationId}`, 0, { + organizationId: query.organizationId + }); + const organization = await this.orgRepo.findById(query.organizationId); + if (!organization) { + (0, Logger_1.logWarning)('Organization not found for login URL request', { + organizationId: query.organizationId + }); + return null; + } + if (!organization.url) { + (0, Logger_1.logWarning)('Organization has no configured login URL', { + organizationId: query.organizationId, + organizationName: organization.name + }); + return null; + } + const result = { + organizationId: organization.id, + organizationName: organization.name, + loginUrl: organization.url + }; + (0, Logger_1.logDatabase)('Organization login URL retrieved successfully', `organizationId: ${query.organizationId}`, Date.now() - startTime, { + organizationId: organization.id, + organizationName: organization.name, + hasUrl: !!organization.url + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('GetOrganizationLoginUrlQueryHandler error', error); + throw new Error('Failed to retrieve organization login URL'); + } + } +} +exports.GetOrganizationLoginUrlQueryHandler = GetOrganizationLoginUrlQueryHandler; +//# sourceMappingURL=GetOrganizationLoginUrlQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.js.map new file mode 100644 index 00000000..ae1199ee --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationLoginUrlQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationLoginUrlQueryHandler.ts"],"names":[],"mappings":";;;AAGA,kDAA0E;AAE1E,MAAa,mCAAmC;IAC9C,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,KAAmC;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,IAAI,CAAC;YACH,IAAA,oBAAW,EAAC,gCAAgC,EAAE,mBAAmB,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE;gBAC1F,cAAc,EAAE,KAAK,CAAC,cAAc;aACrC,CAAC,CAAC;YAEH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YAEvE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,IAAA,mBAAU,EAAC,8CAA8C,EAAE;oBACzD,cAAc,EAAE,KAAK,CAAC,cAAc;iBACrC,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;gBACtB,IAAA,mBAAU,EAAC,0CAA0C,EAAE;oBACrD,cAAc,EAAE,KAAK,CAAC,cAAc;oBACpC,gBAAgB,EAAE,YAAY,CAAC,IAAI;iBACpC,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,MAAM,GAA4B;gBACtC,cAAc,EAAE,YAAY,CAAC,EAAE;gBAC/B,gBAAgB,EAAE,YAAY,CAAC,IAAI;gBACnC,QAAQ,EAAE,YAAY,CAAC,GAAG;aAC3B,CAAC;YAEF,IAAA,oBAAW,EAAC,+CAA+C,EACzD,mBAAmB,KAAK,CAAC,cAAc,EAAE,EACzC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,EACtB;gBACE,cAAc,EAAE,YAAY,CAAC,EAAE;gBAC/B,gBAAgB,EAAE,YAAY,CAAC,IAAI;gBACnC,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG;aAC3B,CACF,CAAC;YAEF,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,2CAA2C,EAAE,KAAc,CAAC,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;CACF;AAlDD,kFAkDC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.d.ts b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.d.ts new file mode 100644 index 00000000..82834da5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.d.ts @@ -0,0 +1,6 @@ +export interface GetOrganizationsByPageQuery { + from: number; + to: number; + includeDeleted?: boolean; +} +//# sourceMappingURL=GetOrganizationsByPageQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.d.ts.map new file mode 100644 index 00000000..52ed8682 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationsByPageQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationsByPageQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,2BAA2B;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.js b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.js new file mode 100644 index 00000000..797f5673 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetOrganizationsByPageQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.js.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.js.map new file mode 100644 index 00000000..ec54281b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationsByPageQuery.js","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationsByPageQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.d.ts new file mode 100644 index 00000000..ec28fc20 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.d.ts @@ -0,0 +1,12 @@ +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { GetOrganizationsByPageQuery } from './GetOrganizationsByPageQuery'; +import { ShortOrganizationDto } from '../../DTOs/OrganizationDto'; +export declare class GetOrganizationsByPageQueryHandler { + private readonly orgRepo; + constructor(orgRepo: IOrganizationRepository); + execute(query: GetOrganizationsByPageQuery): Promise<{ + organizations: ShortOrganizationDto[]; + totalCount: number; + }>; +} +//# sourceMappingURL=GetOrganizationsByPageQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.d.ts.map new file mode 100644 index 00000000..46c016d0 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationsByPageQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationsByPageQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAIlE,qBAAa,kCAAkC;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,OAAO,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,oBAAoB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAkD1H"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.js b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.js new file mode 100644 index 00000000..a46bcf2d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.js @@ -0,0 +1,55 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetOrganizationsByPageQueryHandler = void 0; +const OrganizationMapper_1 = require("../../DTOs/Mappers/OrganizationMapper"); +const Logger_1 = require("../../Services/Logger"); +class GetOrganizationsByPageQueryHandler { + constructor(orgRepo) { + this.orgRepo = orgRepo; + } + async execute(query) { + try { + // Validate pagination parameters + if (query.from < 0 || query.to < query.from) { + throw new Error('Invalid pagination parameters'); + } + const limit = query.to - query.from + 1; + if (limit > 100) { + throw new Error('Page size too large. Maximum 100 records per request'); + } + (0, Logger_1.logRequest)('Get organizations by page query started', undefined, undefined, { + from: query.from, + to: query.to, + includeDeleted: query.includeDeleted || false + }); + const result = query.includeDeleted + ? await this.orgRepo.findByPageIncludingDeleted(query.from, query.to) + : await this.orgRepo.findByPage(query.from, query.to); + (0, Logger_1.logRequest)('Get organizations by page query completed', undefined, undefined, { + from: query.from, + to: query.to, + returned: result.organizations.length, + totalCount: result.totalCount, + includeDeleted: query.includeDeleted || false + }); + return { + organizations: OrganizationMapper_1.OrganizationMapper.toShortDtoList(result.organizations), + totalCount: result.totalCount + }; + } + catch (error) { + (0, Logger_1.logError)('GetOrganizationsByPageQueryHandler error', error instanceof Error ? error : new Error(String(error))); + // Handle database errors + if (error instanceof Error && error.message.includes('database')) { + throw new Error('Database connection error'); + } + // Re-throw validation errors as-is + if (error instanceof Error && (error.message.includes('Invalid pagination') || error.message.includes('Page size'))) { + throw error; + } + throw new Error('Failed to retrieve organizations'); + } + } +} +exports.GetOrganizationsByPageQueryHandler = GetOrganizationsByPageQueryHandler; +//# sourceMappingURL=GetOrganizationsByPageQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.js.map b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.js.map new file mode 100644 index 00000000..6b154729 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Organization/queries/GetOrganizationsByPageQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetOrganizationsByPageQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/Organization/queries/GetOrganizationsByPageQueryHandler.ts"],"names":[],"mappings":";;;AAGA,8EAA2E;AAC3E,kDAA6D;AAE7D,MAAa,kCAAkC;IAC7C,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,KAAkC;QAC9C,IAAI,CAAC;YACH,iCAAiC;YACjC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YACxC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;YAC1E,CAAC;YAED,IAAA,mBAAU,EAAC,yCAAyC,EAAE,SAAS,EAAE,SAAS,EAAE;gBAC1E,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;aAC9C,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc;gBACjC,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACrE,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAExD,IAAA,mBAAU,EAAC,2CAA2C,EAAE,SAAS,EAAE,SAAS,EAAE;gBAC5E,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,QAAQ,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM;gBACrC,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;aAC9C,CAAC,CAAC;YAEH,OAAO;gBACL,aAAa,EAAE,uCAAkB,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC;gBACtE,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,0CAA0C,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEhH,yBAAyB;YACzB,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC/C,CAAC;YAED,mCAAmC;YACnC,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBACpH,MAAM,KAAK,CAAC;YACd,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;CACF;AArDD,gFAqDC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Search/Generalsearch.d.ts b/SerpentRace_Backend/dist/Application/Search/Generalsearch.d.ts new file mode 100644 index 00000000..03a23433 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Search/Generalsearch.d.ts @@ -0,0 +1,27 @@ +import { IUserRepository } from '../../Domain/IRepository/IUserRepository'; +import { IOrganizationRepository } from '../../Domain/IRepository/IOrganizationRepository'; +import { IDeckRepository } from '../../Domain/IRepository/IDeckRepository'; +import { SearchQuery, SearchResult } from '../DTOs/SearchDto'; +import { ShortUserDto } from '../DTOs/UserDto'; +import { ShortOrganizationDto } from '../DTOs/OrganizationDto'; +import { ShortDeckDto } from '../DTOs/DeckDto'; +export type SearchType = 'users' | 'organizations' | 'decks'; +export interface IGeneralSearchService { + searchUsers(searchQuery: SearchQuery): Promise>; + searchOrganizations(searchQuery: SearchQuery): Promise>; + searchDecks(searchQuery: SearchQuery): Promise>; + searchByType(searchType: SearchType, searchQuery: SearchQuery): Promise>; +} +export declare class GeneralSearchService implements IGeneralSearchService { + private userRepo; + private organizationRepo; + private deckRepo; + constructor(userRepo: IUserRepository, organizationRepo: IOrganizationRepository, deckRepo: IDeckRepository); + static getSearchTypeFromUrl(url: string): SearchType; + searchUsers(searchQuery: SearchQuery): Promise>; + searchOrganizations(searchQuery: SearchQuery): Promise>; + searchDecks(searchQuery: SearchQuery): Promise>; + searchByType(searchType: SearchType, searchQuery: SearchQuery): Promise>; + searchFromUrl(url: string, searchQuery: SearchQuery): Promise>; +} +//# sourceMappingURL=Generalsearch.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Search/Generalsearch.d.ts.map b/SerpentRace_Backend/dist/Application/Search/Generalsearch.d.ts.map new file mode 100644 index 00000000..b9dabc25 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Search/Generalsearch.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Generalsearch.d.ts","sourceRoot":"","sources":["../../../src/Application/Search/Generalsearch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAiB,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAyB,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAiB,MAAM,iBAAiB,CAAC;AAK9D,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,eAAe,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IAC3E,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC3F,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IAC3E,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,YAAY,GAAG,oBAAoB,GAAG,YAAY,CAAC,CAAC,CAAC;CAC3I;AAED,qBAAa,oBAAqB,YAAW,qBAAqB;IAE9D,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,QAAQ;gBAFR,QAAQ,EAAE,eAAe,EACzB,gBAAgB,EAAE,uBAAuB,EACzC,QAAQ,EAAE,eAAe;IAGnC,MAAM,CAAC,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU;IAW9C,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IA8B1E,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,oBAAoB,CAAC,CAAC;IA0B1F,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IA0B1E,YAAY,CAChB,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,YAAY,CAAC,YAAY,GAAG,oBAAoB,GAAG,YAAY,CAAC,CAAC;IAatE,aAAa,CACjB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,YAAY,CAAC,YAAY,GAAG,oBAAoB,GAAG,YAAY,CAAC,CAAC;CAI7E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Search/Generalsearch.js b/SerpentRace_Backend/dist/Application/Search/Generalsearch.js new file mode 100644 index 00000000..c21a0b15 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Search/Generalsearch.js @@ -0,0 +1,114 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GeneralSearchService = void 0; +const UserMapper_1 = require("../DTOs/Mappers/UserMapper"); +const OrganizationMapper_1 = require("../DTOs/Mappers/OrganizationMapper"); +const DeckMapper_1 = require("../DTOs/Mappers/DeckMapper"); +class GeneralSearchService { + constructor(userRepo, organizationRepo, deckRepo) { + this.userRepo = userRepo; + this.organizationRepo = organizationRepo; + this.deckRepo = deckRepo; + } + static getSearchTypeFromUrl(url) { + if (url.includes('/users/') || url.includes('/api/users/')) { + return 'users'; + } + else if (url.includes('/organizations/') || url.includes('/api/organizations/')) { + return 'organizations'; + } + else if (url.includes('/decks/') || url.includes('/api/decks/')) { + return 'decks'; + } + return 'users'; + } + async searchUsers(searchQuery) { + const { query, limit = 20, offset = 0 } = searchQuery; + if (!query || query.trim().length === 0) { + return { + results: [], + totalCount: 0, + hasMore: false, + searchQuery: query, + searchType: 'users' + }; + } + try { + const { users, totalCount } = await this.userRepo.search(query.trim(), limit, offset); + const results = users.map(user => UserMapper_1.UserMapper.toShortDto(user)); + const hasMore = (offset + limit) < totalCount; + return { + results, + totalCount, + hasMore, + searchQuery: query, + searchType: 'users' + }; + } + catch (error) { + throw new Error('Failed to search users'); + } + } + async searchOrganizations(searchQuery) { + const { query, limit = 20, offset = 0 } = searchQuery; + if (!query || query.trim().length === 0) { + return { + results: [], + totalCount: 0, + hasMore: false, + searchQuery: query, + searchType: 'organizations' + }; + } + const { organizations, totalCount } = await this.organizationRepo.search(query.trim(), limit, offset); + const results = organizations.map(org => OrganizationMapper_1.OrganizationMapper.toShortDto(org)); + const hasMore = (offset + limit) < totalCount; + return { + results, + totalCount, + hasMore, + searchQuery: query, + searchType: 'organizations' + }; + } + async searchDecks(searchQuery) { + const { query, limit = 20, offset = 0 } = searchQuery; + if (!query || query.trim().length === 0) { + return { + results: [], + totalCount: 0, + hasMore: false, + searchQuery: query, + searchType: 'decks' + }; + } + const { decks, totalCount } = await this.deckRepo.search(query.trim(), limit, offset); + const results = decks.map(deck => DeckMapper_1.DeckMapper.toShortDto(deck)); + const hasMore = (offset + limit) < totalCount; + return { + results, + totalCount, + hasMore, + searchQuery: query, + searchType: 'decks' + }; + } + async searchByType(searchType, searchQuery) { + switch (searchType) { + case 'users': + return await this.searchUsers(searchQuery); + case 'organizations': + return await this.searchOrganizations(searchQuery); + case 'decks': + return await this.searchDecks(searchQuery); + default: + throw new Error(`Unsupported search type: ${searchType}`); + } + } + async searchFromUrl(url, searchQuery) { + const searchType = GeneralSearchService.getSearchTypeFromUrl(url); + return await this.searchByType(searchType, searchQuery); + } +} +exports.GeneralSearchService = GeneralSearchService; +//# sourceMappingURL=Generalsearch.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Search/Generalsearch.js.map b/SerpentRace_Backend/dist/Application/Search/Generalsearch.js.map new file mode 100644 index 00000000..7dfc0555 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Search/Generalsearch.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Generalsearch.js","sourceRoot":"","sources":["../../../src/Application/Search/Generalsearch.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AdminBypassService.d.ts b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.d.ts new file mode 100644 index 00000000..5f900fd2 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.d.ts @@ -0,0 +1,61 @@ +import { UserState } from '../../Domain/User/UserAggregate'; +import { Request, Response } from 'express'; +/** + * Admin Bypass Service - Centralized admin privilege checking and logging + */ +export declare class AdminBypassService { + /** + * Check if user has admin privileges + * @param userState - User's current state + * @returns true if user is admin + */ + static isAdmin(userState: UserState): boolean; + /** + * Check if user should bypass all restrictions + * @param userState - User's current state + * @returns true if restrictions should be bypassed + */ + static shouldBypassRestrictions(userState: UserState): boolean; + /** + * Log admin bypass action for audit trail + * @param action - Description of the action being bypassed + * @param adminUserId - ID of the admin user + * @param targetId - ID of the target resource + * @param details - Additional details about the bypass + * @param req - Optional request object for context + * @param res - Optional response object for context + */ + static logAdminBypass(action: string, adminUserId: string, targetId: string, details?: any, req?: Request, res?: Response): void; +} +/** + * Admin Audit Service - Enhanced logging for all admin actions + */ +export declare class AdminAuditService { + /** + * Log comprehensive admin action for audit trail + * @param action - Action being performed + * @param adminUserId - ID of the admin user + * @param details - Detailed information about the action + * @param req - Request object for context + * @param res - Response object for context + */ + static logAdminAction(action: string, adminUserId: string, details: { + targetType: 'user' | 'organization' | 'deck' | 'contact' | 'chat'; + targetId: string; + operation: 'create' | 'read' | 'update' | 'delete' | 'bypass' | 'export' | 'import'; + changes?: any; + sensitive?: boolean; + metadata?: any; + }, req?: Request, res?: Response): void; + /** + * Log bulk admin operations + * @param action - Bulk action being performed + * @param adminUserId - ID of the admin user + * @param affectedCount - Number of resources affected + * @param targetType - Type of resources affected + * @param req - Request object for context + * @param res - Response object for context + */ + static logBulkAdminAction(action: string, adminUserId: string, affectedCount: number, targetType: string, req?: Request, res?: Response): void; +} +//# sourceMappingURL=AdminBypassService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AdminBypassService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.d.ts.map new file mode 100644 index 00000000..0c01c86b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"AdminBypassService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/AdminBypassService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C;;GAEG;AACH,qBAAa,kBAAkB;IAC3B;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO;IAI7C;;;;OAIG;IACH,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO;IAI9D;;;;;;;;OAQG;IACH,MAAM,CAAC,cAAc,CACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,GAAG,EACb,GAAG,CAAC,EAAE,OAAO,EACb,GAAG,CAAC,EAAE,QAAQ,GACf,IAAI;CASV;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,cAAc,CACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;QACL,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;QAClE,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;QACpF,OAAO,CAAC,EAAE,GAAG,CAAC;QACd,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,QAAQ,CAAC,EAAE,GAAG,CAAC;KAClB,EACD,GAAG,CAAC,EAAE,OAAO,EACb,GAAG,CAAC,EAAE,QAAQ,GACf,IAAI;IA2BP;;;;;;;;OAQG;IACH,MAAM,CAAC,kBAAkB,CACrB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,OAAO,EACb,GAAG,CAAC,EAAE,QAAQ,GACf,IAAI;CASV"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AdminBypassService.js b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.js new file mode 100644 index 00000000..82f206d1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.js @@ -0,0 +1,101 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminAuditService = exports.AdminBypassService = void 0; +const UserAggregate_1 = require("../../Domain/User/UserAggregate"); +const Logger_1 = require("./Logger"); +/** + * Admin Bypass Service - Centralized admin privilege checking and logging + */ +class AdminBypassService { + /** + * Check if user has admin privileges + * @param userState - User's current state + * @returns true if user is admin + */ + static isAdmin(userState) { + return userState === UserAggregate_1.UserState.ADMIN; + } + /** + * Check if user should bypass all restrictions + * @param userState - User's current state + * @returns true if restrictions should be bypassed + */ + static shouldBypassRestrictions(userState) { + return this.isAdmin(userState); + } + /** + * Log admin bypass action for audit trail + * @param action - Description of the action being bypassed + * @param adminUserId - ID of the admin user + * @param targetId - ID of the target resource + * @param details - Additional details about the bypass + * @param req - Optional request object for context + * @param res - Optional response object for context + */ + static logAdminBypass(action, adminUserId, targetId, details, req, res) { + (0, Logger_1.logAuth)(`ADMIN_BYPASS: ${action}`, adminUserId, { + targetId, + action, + bypassReason: 'Admin privileges', + timestamp: new Date().toISOString(), + ...details + }, req, res); + } +} +exports.AdminBypassService = AdminBypassService; +/** + * Admin Audit Service - Enhanced logging for all admin actions + */ +class AdminAuditService { + /** + * Log comprehensive admin action for audit trail + * @param action - Action being performed + * @param adminUserId - ID of the admin user + * @param details - Detailed information about the action + * @param req - Request object for context + * @param res - Response object for context + */ + static logAdminAction(action, adminUserId, details, req, res) { + const auditData = { + timestamp: new Date().toISOString(), + adminUserId, + action, + ...details, + ip: req?.ip, + userAgent: req?.get('User-Agent'), + endpoint: req?.path, + method: req?.method, + requestId: req?.headers['x-request-id'] || 'unknown' + }; + // Enhanced logging for admin actions + (0, Logger_1.logAuth)(`ADMIN_AUDIT: ${action}`, adminUserId, auditData, req, res); + // Additional security logging for sensitive operations + if (details.sensitive) { + (0, Logger_1.logAuth)(`ADMIN_SENSITIVE: ${action}`, adminUserId, { + ...auditData, + alertLevel: 'HIGH', + requiresReview: true + }, req, res); + } + } + /** + * Log bulk admin operations + * @param action - Bulk action being performed + * @param adminUserId - ID of the admin user + * @param affectedCount - Number of resources affected + * @param targetType - Type of resources affected + * @param req - Request object for context + * @param res - Response object for context + */ + static logBulkAdminAction(action, adminUserId, affectedCount, targetType, req, res) { + this.logAdminAction(`BULK_${action}`, adminUserId, { + targetType: targetType, + targetId: `bulk-${affectedCount}-items`, + operation: 'update', + metadata: { affectedCount }, + sensitive: affectedCount > 10 // Mark large bulk operations as sensitive + }, req, res); + } +} +exports.AdminAuditService = AdminAuditService; +//# sourceMappingURL=AdminBypassService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AdminBypassService.js.map b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.js.map new file mode 100644 index 00000000..a39f763e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AdminBypassService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AdminBypassService.js","sourceRoot":"","sources":["../../../src/Application/Services/AdminBypassService.ts"],"names":[],"mappings":";;;AAAA,mEAA4D;AAC5D,qCAAmC;AAGnC;;GAEG;AACH,MAAa,kBAAkB;IAC3B;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,SAAoB;QAC/B,OAAO,SAAS,KAAK,yBAAS,CAAC,KAAK,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,wBAAwB,CAAC,SAAoB;QAChD,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,cAAc,CACjB,MAAc,EACd,WAAmB,EACnB,QAAgB,EAChB,OAAa,EACb,GAAa,EACb,GAAc;QAEd,IAAA,gBAAO,EAAC,iBAAiB,MAAM,EAAE,EAAE,WAAW,EAAE;YAC5C,QAAQ;YACR,MAAM;YACN,YAAY,EAAE,kBAAkB;YAChC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,GAAG,OAAO;SACb,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACjB,CAAC;CACJ;AA5CD,gDA4CC;AAED;;GAEG;AACH,MAAa,iBAAiB;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,cAAc,CACjB,MAAc,EACd,WAAmB,EACnB,OAOC,EACD,GAAa,EACb,GAAc;QAGd,MAAM,SAAS,GAAG;YACd,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,WAAW;YACX,MAAM;YACN,GAAG,OAAO;YACV,EAAE,EAAE,GAAG,EAAE,EAAE;YACX,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC;YACjC,QAAQ,EAAE,GAAG,EAAE,IAAI;YACnB,MAAM,EAAE,GAAG,EAAE,MAAM;YACnB,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,IAAI,SAAS;SACvD,CAAC;QAEF,qCAAqC;QACrC,IAAA,gBAAO,EAAC,gBAAgB,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAEpE,uDAAuD;QACvD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACpB,IAAA,gBAAO,EAAC,oBAAoB,MAAM,EAAE,EAAE,WAAW,EAAE;gBAC/C,GAAG,SAAS;gBACZ,UAAU,EAAE,MAAM;gBAClB,cAAc,EAAE,IAAI;aACvB,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACjB,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,kBAAkB,CACrB,MAAc,EACd,WAAmB,EACnB,aAAqB,EACrB,UAAkB,EAClB,GAAa,EACb,GAAc;QAEd,IAAI,CAAC,cAAc,CAAC,QAAQ,MAAM,EAAE,EAAE,WAAW,EAAE;YAC/C,UAAU,EAAE,UAAiB;YAC7B,QAAQ,EAAE,QAAQ,aAAa,QAAQ;YACvC,SAAS,EAAE,QAAe;YAC1B,QAAQ,EAAE,EAAE,aAAa,EAAE;YAC3B,SAAS,EAAE,aAAa,GAAG,EAAE,CAAC,0CAA0C;SAC3E,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACjB,CAAC;CACJ;AA1ED,8CA0EC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.d.ts b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.d.ts new file mode 100644 index 00000000..f7dce2a8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.d.ts @@ -0,0 +1,6 @@ +import { Request, Response, NextFunction } from 'express'; +import { JWTService } from './JWTService'; +export declare const jwtService: JWTService; +export declare function authRequired(req: Request, res: Response, next: NextFunction): Response> | undefined; +export declare function adminRequired(req: Request, res: Response, next: NextFunction): Response> | undefined; +//# sourceMappingURL=AuthMiddleware.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.d.ts.map b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.d.ts.map new file mode 100644 index 00000000..c2a3f7f0 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"AuthMiddleware.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/AuthMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,eAAO,MAAM,UAAU,YAAmB,CAAC;AAE3C,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,kDAuB3E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,kDAyB5E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.js b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.js new file mode 100644 index 00000000..995d747d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jwtService = void 0; +exports.authRequired = authRequired; +exports.adminRequired = adminRequired; +const JWTService_1 = require("./JWTService"); +const Logger_1 = require("./Logger"); +exports.jwtService = new JWTService_1.JWTService(); +function authRequired(req, res, next) { + const payload = exports.jwtService.verify(req); + if (!payload) { + (0, Logger_1.logAuth)('Authentication failed - No valid token', undefined, { + ip: req.ip, + userAgent: req.get ? req.get('User-Agent') : 'unknown', + path: req.path + }, req); + return res.status(401).json({ error: 'Unauthorized' }); + } + (0, Logger_1.logAuth)('Authentication successful', payload.userId, { + authLevel: payload.authLevel, + orgId: payload.orgId + }, req); + const refreshed = exports.jwtService.refreshIfNeeded(payload, res); + if (refreshed) { + (0, Logger_1.logAuth)('Token refreshed', payload.userId, undefined, req); + } + req.user = payload; + next(); +} +function adminRequired(req, res, next) { + const payload = exports.jwtService.verify(req); + if (!payload || payload.authLevel !== 1) { + (0, Logger_1.logWarning)('Admin access denied', { + hasPayload: !!payload, + authLevel: payload?.authLevel, + userId: payload?.userId, + ip: req.ip, + path: req.path + }, req); + return res.status(403).json({ error: 'Forbidden' }); + } + (0, Logger_1.logAuth)('Admin authentication successful', payload.userId, { + authLevel: payload.authLevel, + orgId: payload.orgId + }, req); + const refreshed = exports.jwtService.refreshIfNeeded(payload, res); + if (refreshed) { + (0, Logger_1.logAuth)('Admin token refreshed', payload.userId, undefined, req); + } + req.user = payload; + next(); +} +//# sourceMappingURL=AuthMiddleware.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.js.map b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.js.map new file mode 100644 index 00000000..4e66cae6 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/AuthMiddleware.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AuthMiddleware.js","sourceRoot":"","sources":["../../../src/Application/Services/AuthMiddleware.ts"],"names":[],"mappings":";;;AAMA,oCAuBC;AAED,sCAyBC;AAvDD,6CAA0C;AAC1C,qCAA+C;AAElC,QAAA,UAAU,GAAG,IAAI,uBAAU,EAAE,CAAC;AAE3C,SAAgB,YAAY,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACxE,MAAM,OAAO,GAAG,kBAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,IAAA,gBAAO,EAAC,wCAAwC,EAAE,SAAS,EAAE;YACzD,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;YACtD,IAAI,EAAE,GAAG,CAAC,IAAI;SACjB,EAAE,GAAG,CAAC,CAAC;QACR,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAA,gBAAO,EAAC,2BAA2B,EAAE,OAAO,CAAC,MAAM,EAAE;QACjD,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;KACvB,EAAE,GAAG,CAAC,CAAC;IAER,MAAM,SAAS,GAAG,kBAAU,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC3D,IAAI,SAAS,EAAE,CAAC;QACZ,IAAA,gBAAO,EAAC,iBAAiB,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;IAEA,GAAW,CAAC,IAAI,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,CAAC;AACX,CAAC;AAED,SAAgB,aAAa,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACzE,MAAM,OAAO,GAAG,kBAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;QACtC,IAAA,mBAAU,EAAC,qBAAqB,EAAE;YAC9B,UAAU,EAAE,CAAC,CAAC,OAAO;YACrB,SAAS,EAAE,OAAO,EAAE,SAAS;YAC7B,MAAM,EAAE,OAAO,EAAE,MAAM;YACvB,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;SACjB,EAAE,GAAG,CAAC,CAAC;QACR,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,IAAA,gBAAO,EAAC,iCAAiC,EAAE,OAAO,CAAC,MAAM,EAAE;QACvD,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;KACvB,EAAE,GAAG,CAAC,CAAC;IAER,MAAM,SAAS,GAAG,kBAAU,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC3D,IAAI,SAAS,EAAE,CAAC;QACZ,IAAA,gBAAO,EAAC,uBAAuB,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACrE,CAAC;IAEA,GAAW,CAAC,IAAI,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,CAAC;AACX,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ContactEmailService.d.ts b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.d.ts new file mode 100644 index 00000000..984a761c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.d.ts @@ -0,0 +1,22 @@ +import { IContactRepository } from '../../Domain/IRepository/IContactRepository'; +import { ContactType } from '../../Domain/Contact/ContactAggregate'; +export interface EmailResponseData { + to: string; + message: string; + contactId: string; + adminUserId: string; + contactName: string; + contactType: ContactType; + originalMessage: string; + language?: 'en' | 'hu' | 'de'; +} +export declare class ContactEmailService { + private readonly contactRepo; + private emailService; + constructor(contactRepo: IContactRepository); + sendResponse(responseData: EmailResponseData): Promise; + private getLocalizedContactResponseSubject; + private getContactTypeString; + private getContactTypeBadge; +} +//# sourceMappingURL=ContactEmailService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ContactEmailService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.d.ts.map new file mode 100644 index 00000000..ef7ef956 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactEmailService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/ContactEmailService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AAEjF,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAIpE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC/B;AAED,qBAAa,mBAAmB;IAGlB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFxC,OAAO,CAAC,YAAY,CAAe;gBAEN,WAAW,EAAE,kBAAkB;IAItD,YAAY,CAAC,YAAY,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDlE,OAAO,CAAC,kCAAkC;IAW1C,OAAO,CAAC,oBAAoB;IAgC5B,OAAO,CAAC,mBAAmB;CAgB5B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ContactEmailService.js b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.js new file mode 100644 index 00000000..cb2d41a3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.js @@ -0,0 +1,117 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContactEmailService = void 0; +const EmailService_1 = require("./EmailService"); +const ContactAggregate_1 = require("../../Domain/Contact/ContactAggregate"); +const Logger_1 = require("./Logger"); +const EmailTemplateHelper_1 = require("./EmailTemplateHelper"); +class ContactEmailService { + constructor(contactRepo) { + this.contactRepo = contactRepo; + this.emailService = new EmailService_1.EmailService(); + } + async sendResponse(responseData) { + try { + // First update the contact with the response + await this.contactRepo.update(responseData.contactId, { + adminResponse: responseData.message, + responseDate: new Date(), + respondedBy: responseData.adminUserId, + }); + // Determine language and template + const language = responseData.language || 'en'; + const templateName = language === 'en' ? 'contact-response' : `contact-response-${language}`; + // Prepare template data + const templateData = { + contactName: responseData.contactName, + contactTypeString: this.getContactTypeString(responseData.contactType, language), + contactTypeBadge: this.getContactTypeBadge(responseData.contactType), + originalMessage: responseData.originalMessage, + adminResponse: responseData.message, + companyName: 'SerpentRace', + supportEmail: 'support@serpentrace.com' + }; + // Send email using EmailService with template + const emailSent = await this.emailService.sendEmail({ + to: responseData.to, + subject: this.getLocalizedContactResponseSubject(language), + template: templateName, + templateData + }); + if (emailSent) { + (0, Logger_1.logOther)('Contact response email sent successfully', { + to: responseData.to, + subject: this.getLocalizedContactResponseSubject(language), + contactId: responseData.contactId, + respondedBy: responseData.adminUserId, + language + }); + } + else { + throw new Error('Email service failed to send email'); + } + } + catch (error) { + (0, Logger_1.logError)('Failed to send contact response email', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to send email response'); + } + } + getLocalizedContactResponseSubject(language) { + const subjects = { + contactResponse: { + en: 'SerpentRace - Response to Your Message', + hu: 'SerpentRace - Válasz az üzenetére', + de: 'SerpentRace - Antwort auf Ihre Nachricht' + } + }; + return EmailTemplateHelper_1.EmailTemplateHelper.getLocalizedSubject('contactResponse', subjects, language); + } + getContactTypeString(type, language = 'en') { + const translations = { + [ContactAggregate_1.ContactType.BUG]: { + en: 'Bug Report', + hu: 'Hiba bejelentés', + de: 'Fehlerbericht' + }, + [ContactAggregate_1.ContactType.PROBLEM]: { + en: 'Problem', + hu: 'Probléma', + de: 'Problem' + }, + [ContactAggregate_1.ContactType.QUESTION]: { + en: 'Question', + hu: 'Kérdés', + de: 'Frage' + }, + [ContactAggregate_1.ContactType.SALES]: { + en: 'Sales Inquiry', + hu: 'Értékesítési kérdés', + de: 'Verkaufsanfrage' + }, + [ContactAggregate_1.ContactType.OTHER]: { + en: 'General Inquiry', + hu: 'Általános kérdés', + de: 'Allgemeine Anfrage' + } + }; + return translations[type]?.[language] || translations[type]?.['en'] || 'Contact'; + } + getContactTypeBadge(type) { + switch (type) { + case ContactAggregate_1.ContactType.BUG: + return 'bug'; + case ContactAggregate_1.ContactType.PROBLEM: + return 'problem'; + case ContactAggregate_1.ContactType.QUESTION: + return 'question'; + case ContactAggregate_1.ContactType.SALES: + return 'sales'; + case ContactAggregate_1.ContactType.OTHER: + return 'other'; + default: + return 'other'; + } + } +} +exports.ContactEmailService = ContactEmailService; +//# sourceMappingURL=ContactEmailService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ContactEmailService.js.map b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.js.map new file mode 100644 index 00000000..49003197 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ContactEmailService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactEmailService.js","sourceRoot":"","sources":["../../../src/Application/Services/ContactEmailService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DIContainer.d.ts b/SerpentRace_Backend/dist/Application/Services/DIContainer.d.ts new file mode 100644 index 00000000..1e3a1aef --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DIContainer.d.ts @@ -0,0 +1,141 @@ +import { IUserRepository } from '../../Domain/IRepository/IUserRepository'; +import { IChatRepository } from '../../Domain/IRepository/IChatRepository'; +import { IChatArchiveRepository } from '../../Domain/IRepository/IChatArchiveRepository'; +import { IDeckRepository } from '../../Domain/IRepository/IDeckRepository'; +import { IOrganizationRepository } from '../../Domain/IRepository/IOrganizationRepository'; +import { IContactRepository } from '../../Domain/IRepository/IContactRepository'; +import { CreateUserCommandHandler } from '../User/commands/CreateUserCommandHandler'; +import { LoginCommandHandler } from '../User/commands/LoginCommandHandler'; +import { UpdateUserCommandHandler } from '../User/commands/UpdateUserCommandHandler'; +import { DeactivateUserCommandHandler } from '../User/commands/DeactivateUserCommandHandler'; +import { DeleteUserCommandHandler } from '../User/commands/DeleteUserCommandHandler'; +import { VerifyEmailCommandHandler } from '../User/commands/VerifyEmailCommandHandler'; +import { RequestPasswordResetCommandHandler } from '../User/commands/RequestPasswordResetCommandHandler'; +import { ResetPasswordCommandHandler } from '../User/commands/ResetPasswordCommandHandler'; +import { CreateChatCommandHandler } from '../Chat/commands/CreateChatCommandHandler'; +import { SendMessageCommandHandler } from '../Chat/commands/SendMessageCommandHandler'; +import { ArchiveChatCommandHandler, RestoreChatCommandHandler } from '../Chat/commands/ChatArchiveCommandHandlers'; +import { CreateDeckCommandHandler } from '../Deck/commands/CreateDeckCommandHandler'; +import { UpdateDeckCommandHandler } from '../Deck/commands/UpdateDeckCommandHandler'; +import { DeleteDeckCommandHandler } from '../Deck/commands/DeleteDeckCommandHandler'; +import { CreateOrganizationCommandHandler } from '../Organization/commands/CreateOrganizationCommandHandler'; +import { UpdateOrganizationCommandHandler } from '../Organization/commands/UpdateOrganizationCommandHandler'; +import { DeleteOrganizationCommandHandler } from '../Organization/commands/DeleteOrganizationCommandHandler'; +import { ProcessOrgAuthCallbackCommandHandler } from '../Organization/commands/ProcessOrgAuthCallbackCommandHandler'; +import { CreateContactCommandHandler } from '../Contact/commands/CreateContactCommandHandler'; +import { UpdateContactCommandHandler } from '../Contact/commands/UpdateContactCommandHandler'; +import { DeleteContactCommandHandler } from '../Contact/commands/DeleteContactCommandHandler'; +import { GetUserByIdQueryHandler } from '../User/queries/GetUserByIdQueryHandler'; +import { GetUsersByPageQueryHandler } from '../User/queries/GetUsersByPageQueryHandler'; +import { GetUserChatsQueryHandler } from '../Chat/queries/GetUserChatsQueryHandler'; +import { GetChatHistoryQueryHandler, GetArchivedChatsQueryHandler } from '../Chat/queries/ChatHistoryQueryHandlers'; +import { GetChatsByPageQueryHandler } from '../Chat/queries/GetChatsByPageQueryHandler'; +import { GetDeckByIdQueryHandler } from '../Deck/queries/GetDeckByIdQueryHandler'; +import { GetDecksByPageQueryHandler } from '../Deck/queries/GetDecksByPageQueryHandler'; +import { GetOrganizationByIdQueryHandler } from '../Organization/queries/GetOrganizationByIdQueryHandler'; +import { GetOrganizationsByPageQueryHandler } from '../Organization/queries/GetOrganizationsByPageQueryHandler'; +import { GetOrganizationLoginUrlQueryHandler } from '../Organization/queries/GetOrganizationLoginUrlQueryHandler'; +import { GetContactByIdQueryHandler } from '../Contact/queries/GetContactByIdQueryHandler'; +import { GetContactsByPageQueryHandler } from '../Contact/queries/GetContactsByPageQueryHandler'; +import { JWTService } from './JWTService'; +import { ContactEmailService } from './ContactEmailService'; +import { DeckImportExportService } from './DeckImportExportService'; +/** + * Central Dependency Injection Container + * Manages all repositories, command handlers, and query handlers as singletons + */ +export declare class DIContainer { + private static instance; + private _userRepository; + private _chatRepository; + private _chatArchiveRepository; + private _deckRepository; + private _organizationRepository; + private _contactRepository; + private _jwtService; + private _contactEmailService; + private _deckImportExportService; + private _createUserCommandHandler; + private _loginCommandHandler; + private _updateUserCommandHandler; + private _deactivateUserCommandHandler; + private _deleteUserCommandHandler; + private _verifyEmailCommandHandler; + private _requestPasswordResetCommandHandler; + private _resetPasswordCommandHandler; + private _createChatCommandHandler; + private _sendMessageCommandHandler; + private _archiveChatCommandHandler; + private _restoreChatCommandHandler; + private _createDeckCommandHandler; + private _updateDeckCommandHandler; + private _deleteDeckCommandHandler; + private _createOrganizationCommandHandler; + private _updateOrganizationCommandHandler; + private _deleteOrganizationCommandHandler; + private _processOrgAuthCallbackCommandHandler; + private _createContactCommandHandler; + private _updateContactCommandHandler; + private _deleteContactCommandHandler; + private _getUserByIdQueryHandler; + private _getUsersByPageQueryHandler; + private _getUserChatsQueryHandler; + private _getChatHistoryQueryHandler; + private _getArchivedChatsQueryHandler; + private _getChatsByPageQueryHandler; + private _getDeckByIdQueryHandler; + private _getDecksByPageQueryHandler; + private _getOrganizationByIdQueryHandler; + private _getOrganizationsByPageQueryHandler; + private _getOrganizationLoginUrlQueryHandler; + private _getContactByIdQueryHandler; + private _getContactsByPageQueryHandler; + private constructor(); + static getInstance(): DIContainer; + get userRepository(): IUserRepository; + get chatRepository(): IChatRepository; + get chatArchiveRepository(): IChatArchiveRepository; + get deckRepository(): IDeckRepository; + get organizationRepository(): IOrganizationRepository; + get contactRepository(): IContactRepository; + get jwtService(): JWTService; + get contactEmailService(): ContactEmailService; + get deckImportExportService(): DeckImportExportService; + get createUserCommandHandler(): CreateUserCommandHandler; + get loginCommandHandler(): LoginCommandHandler; + get updateUserCommandHandler(): UpdateUserCommandHandler; + get deactivateUserCommandHandler(): DeactivateUserCommandHandler; + get deleteUserCommandHandler(): DeleteUserCommandHandler; + get verifyEmailCommandHandler(): VerifyEmailCommandHandler; + get requestPasswordResetCommandHandler(): RequestPasswordResetCommandHandler; + get resetPasswordCommandHandler(): ResetPasswordCommandHandler; + get createChatCommandHandler(): CreateChatCommandHandler; + get sendMessageCommandHandler(): SendMessageCommandHandler; + get archiveChatCommandHandler(): ArchiveChatCommandHandler; + get restoreChatCommandHandler(): RestoreChatCommandHandler; + get createDeckCommandHandler(): CreateDeckCommandHandler; + get updateDeckCommandHandler(): UpdateDeckCommandHandler; + get deleteDeckCommandHandler(): DeleteDeckCommandHandler; + get createOrganizationCommandHandler(): CreateOrganizationCommandHandler; + get updateOrganizationCommandHandler(): UpdateOrganizationCommandHandler; + get deleteOrganizationCommandHandler(): DeleteOrganizationCommandHandler; + get processOrgAuthCallbackCommandHandler(): ProcessOrgAuthCallbackCommandHandler; + get createContactCommandHandler(): CreateContactCommandHandler; + get updateContactCommandHandler(): UpdateContactCommandHandler; + get deleteContactCommandHandler(): DeleteContactCommandHandler; + get getUserByIdQueryHandler(): GetUserByIdQueryHandler; + get getUserChatsQueryHandler(): GetUserChatsQueryHandler; + get getChatHistoryQueryHandler(): GetChatHistoryQueryHandler; + get getArchivedChatsQueryHandler(): GetArchivedChatsQueryHandler; + get getDeckByIdQueryHandler(): GetDeckByIdQueryHandler; + get getOrganizationByIdQueryHandler(): GetOrganizationByIdQueryHandler; + get getOrganizationLoginUrlQueryHandler(): GetOrganizationLoginUrlQueryHandler; + get getContactByIdQueryHandler(): GetContactByIdQueryHandler; + get getContactsByPageQueryHandler(): GetContactsByPageQueryHandler; + get getUsersByPageQueryHandler(): GetUsersByPageQueryHandler; + get getDecksByPageQueryHandler(): GetDecksByPageQueryHandler; + get getOrganizationsByPageQueryHandler(): GetOrganizationsByPageQueryHandler; + get getChatsByPageQueryHandler(): GetChatsByPageQueryHandler; +} +export declare const container: DIContainer; +//# sourceMappingURL=DIContainer.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DIContainer.d.ts.map b/SerpentRace_Backend/dist/Application/Services/DIContainer.d.ts.map new file mode 100644 index 00000000..58fbc1bd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DIContainer.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DIContainer.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/DIContainer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DIContainer.js b/SerpentRace_Backend/dist/Application/Services/DIContainer.js new file mode 100644 index 00000000..2d838bf4 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DIContainer.js @@ -0,0 +1,384 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.container = exports.DIContainer = void 0; +// Repository Implementations +const UserRepository_1 = require("../../Infrastructure/Repository/UserRepository"); +const ChatRepository_1 = require("../../Infrastructure/Repository/ChatRepository"); +const ChatArchiveRepository_1 = require("../../Infrastructure/Repository/ChatArchiveRepository"); +const DeckRepository_1 = require("../../Infrastructure/Repository/DeckRepository"); +const OrganizationRepository_1 = require("../../Infrastructure/Repository/OrganizationRepository"); +const ContactRepository_1 = require("../../Infrastructure/Repository/ContactRepository"); +// Command Handlers +const CreateUserCommandHandler_1 = require("../User/commands/CreateUserCommandHandler"); +const LoginCommandHandler_1 = require("../User/commands/LoginCommandHandler"); +const UpdateUserCommandHandler_1 = require("../User/commands/UpdateUserCommandHandler"); +const DeactivateUserCommandHandler_1 = require("../User/commands/DeactivateUserCommandHandler"); +const DeleteUserCommandHandler_1 = require("../User/commands/DeleteUserCommandHandler"); +const VerifyEmailCommandHandler_1 = require("../User/commands/VerifyEmailCommandHandler"); +const RequestPasswordResetCommandHandler_1 = require("../User/commands/RequestPasswordResetCommandHandler"); +const ResetPasswordCommandHandler_1 = require("../User/commands/ResetPasswordCommandHandler"); +const CreateChatCommandHandler_1 = require("../Chat/commands/CreateChatCommandHandler"); +const SendMessageCommandHandler_1 = require("../Chat/commands/SendMessageCommandHandler"); +const ChatArchiveCommandHandlers_1 = require("../Chat/commands/ChatArchiveCommandHandlers"); +const CreateDeckCommandHandler_1 = require("../Deck/commands/CreateDeckCommandHandler"); +const UpdateDeckCommandHandler_1 = require("../Deck/commands/UpdateDeckCommandHandler"); +const DeleteDeckCommandHandler_1 = require("../Deck/commands/DeleteDeckCommandHandler"); +const CreateOrganizationCommandHandler_1 = require("../Organization/commands/CreateOrganizationCommandHandler"); +const UpdateOrganizationCommandHandler_1 = require("../Organization/commands/UpdateOrganizationCommandHandler"); +const DeleteOrganizationCommandHandler_1 = require("../Organization/commands/DeleteOrganizationCommandHandler"); +const ProcessOrgAuthCallbackCommandHandler_1 = require("../Organization/commands/ProcessOrgAuthCallbackCommandHandler"); +const CreateContactCommandHandler_1 = require("../Contact/commands/CreateContactCommandHandler"); +const UpdateContactCommandHandler_1 = require("../Contact/commands/UpdateContactCommandHandler"); +const DeleteContactCommandHandler_1 = require("../Contact/commands/DeleteContactCommandHandler"); +// Query Handlers +const GetUserByIdQueryHandler_1 = require("../User/queries/GetUserByIdQueryHandler"); +const GetUsersByPageQueryHandler_1 = require("../User/queries/GetUsersByPageQueryHandler"); +const GetUserChatsQueryHandler_1 = require("../Chat/queries/GetUserChatsQueryHandler"); +const ChatHistoryQueryHandlers_1 = require("../Chat/queries/ChatHistoryQueryHandlers"); +const GetChatsByPageQueryHandler_1 = require("../Chat/queries/GetChatsByPageQueryHandler"); +const GetDeckByIdQueryHandler_1 = require("../Deck/queries/GetDeckByIdQueryHandler"); +const GetDecksByPageQueryHandler_1 = require("../Deck/queries/GetDecksByPageQueryHandler"); +const GetOrganizationByIdQueryHandler_1 = require("../Organization/queries/GetOrganizationByIdQueryHandler"); +const GetOrganizationsByPageQueryHandler_1 = require("../Organization/queries/GetOrganizationsByPageQueryHandler"); +const GetOrganizationLoginUrlQueryHandler_1 = require("../Organization/queries/GetOrganizationLoginUrlQueryHandler"); +const GetContactByIdQueryHandler_1 = require("../Contact/queries/GetContactByIdQueryHandler"); +const GetContactsByPageQueryHandler_1 = require("../Contact/queries/GetContactsByPageQueryHandler"); +// Services +const JWTService_1 = require("./JWTService"); +const ContactEmailService_1 = require("./ContactEmailService"); +const DeckImportExportService_1 = require("./DeckImportExportService"); +/** + * Central Dependency Injection Container + * Manages all repositories, command handlers, and query handlers as singletons + */ +class DIContainer { + constructor() { + // Repositories - Using interfaces for better abstraction + this._userRepository = null; + this._chatRepository = null; + this._chatArchiveRepository = null; + this._deckRepository = null; + this._organizationRepository = null; + this._contactRepository = null; + // Services + this._jwtService = null; + this._contactEmailService = null; + this._deckImportExportService = null; + // Command Handlers + this._createUserCommandHandler = null; + this._loginCommandHandler = null; + this._updateUserCommandHandler = null; + this._deactivateUserCommandHandler = null; + this._deleteUserCommandHandler = null; + this._verifyEmailCommandHandler = null; + this._requestPasswordResetCommandHandler = null; + this._resetPasswordCommandHandler = null; + this._createChatCommandHandler = null; + this._sendMessageCommandHandler = null; + this._archiveChatCommandHandler = null; + this._restoreChatCommandHandler = null; + this._createDeckCommandHandler = null; + this._updateDeckCommandHandler = null; + this._deleteDeckCommandHandler = null; + this._createOrganizationCommandHandler = null; + this._updateOrganizationCommandHandler = null; + this._deleteOrganizationCommandHandler = null; + this._processOrgAuthCallbackCommandHandler = null; + this._createContactCommandHandler = null; + this._updateContactCommandHandler = null; + this._deleteContactCommandHandler = null; + // Query Handlers + this._getUserByIdQueryHandler = null; + this._getUsersByPageQueryHandler = null; + this._getUserChatsQueryHandler = null; + this._getChatHistoryQueryHandler = null; + this._getArchivedChatsQueryHandler = null; + this._getChatsByPageQueryHandler = null; + this._getDeckByIdQueryHandler = null; + this._getDecksByPageQueryHandler = null; + this._getOrganizationByIdQueryHandler = null; + this._getOrganizationsByPageQueryHandler = null; + this._getOrganizationLoginUrlQueryHandler = null; + this._getContactByIdQueryHandler = null; + this._getContactsByPageQueryHandler = null; + } + static getInstance() { + if (!DIContainer.instance) { + DIContainer.instance = new DIContainer(); + } + return DIContainer.instance; + } + // Repository getters - Return interfaces for better abstraction + get userRepository() { + if (!this._userRepository) { + this._userRepository = new UserRepository_1.UserRepository(); + } + return this._userRepository; + } + get chatRepository() { + if (!this._chatRepository) { + this._chatRepository = new ChatRepository_1.ChatRepository(); + } + return this._chatRepository; + } + get chatArchiveRepository() { + if (!this._chatArchiveRepository) { + this._chatArchiveRepository = new ChatArchiveRepository_1.ChatArchiveRepository(); + } + return this._chatArchiveRepository; + } + get deckRepository() { + if (!this._deckRepository) { + this._deckRepository = new DeckRepository_1.DeckRepository(); + } + return this._deckRepository; + } + get organizationRepository() { + if (!this._organizationRepository) { + this._organizationRepository = new OrganizationRepository_1.OrganizationRepository(); + } + return this._organizationRepository; + } + get contactRepository() { + if (!this._contactRepository) { + this._contactRepository = new ContactRepository_1.ContactRepository(); + } + return this._contactRepository; + } + // Services getters + get jwtService() { + if (!this._jwtService) { + this._jwtService = new JWTService_1.JWTService(); + } + return this._jwtService; + } + get contactEmailService() { + if (!this._contactEmailService) { + this._contactEmailService = new ContactEmailService_1.ContactEmailService(this.contactRepository); + } + return this._contactEmailService; + } + get deckImportExportService() { + if (!this._deckImportExportService) { + this._deckImportExportService = new DeckImportExportService_1.DeckImportExportService(this.deckRepository); + } + return this._deckImportExportService; + } + // Command Handler getters + get createUserCommandHandler() { + if (!this._createUserCommandHandler) { + this._createUserCommandHandler = new CreateUserCommandHandler_1.CreateUserCommandHandler(this.userRepository); + } + return this._createUserCommandHandler; + } + get loginCommandHandler() { + if (!this._loginCommandHandler) { + this._loginCommandHandler = new LoginCommandHandler_1.LoginCommandHandler(this.userRepository, this.jwtService, this.organizationRepository); + } + return this._loginCommandHandler; + } + get updateUserCommandHandler() { + if (!this._updateUserCommandHandler) { + this._updateUserCommandHandler = new UpdateUserCommandHandler_1.UpdateUserCommandHandler(this.userRepository); + } + return this._updateUserCommandHandler; + } + get deactivateUserCommandHandler() { + if (!this._deactivateUserCommandHandler) { + this._deactivateUserCommandHandler = new DeactivateUserCommandHandler_1.DeactivateUserCommandHandler(this.userRepository); + } + return this._deactivateUserCommandHandler; + } + get deleteUserCommandHandler() { + if (!this._deleteUserCommandHandler) { + this._deleteUserCommandHandler = new DeleteUserCommandHandler_1.DeleteUserCommandHandler(this.userRepository); + } + return this._deleteUserCommandHandler; + } + get verifyEmailCommandHandler() { + if (!this._verifyEmailCommandHandler) { + this._verifyEmailCommandHandler = new VerifyEmailCommandHandler_1.VerifyEmailCommandHandler(this.userRepository); + } + return this._verifyEmailCommandHandler; + } + get requestPasswordResetCommandHandler() { + if (!this._requestPasswordResetCommandHandler) { + this._requestPasswordResetCommandHandler = new RequestPasswordResetCommandHandler_1.RequestPasswordResetCommandHandler(this.userRepository); + } + return this._requestPasswordResetCommandHandler; + } + get resetPasswordCommandHandler() { + if (!this._resetPasswordCommandHandler) { + this._resetPasswordCommandHandler = new ResetPasswordCommandHandler_1.ResetPasswordCommandHandler(this.userRepository); + } + return this._resetPasswordCommandHandler; + } + get createChatCommandHandler() { + if (!this._createChatCommandHandler) { + this._createChatCommandHandler = new CreateChatCommandHandler_1.CreateChatCommandHandler(this.chatRepository, this.userRepository); + } + return this._createChatCommandHandler; + } + get sendMessageCommandHandler() { + if (!this._sendMessageCommandHandler) { + this._sendMessageCommandHandler = new SendMessageCommandHandler_1.SendMessageCommandHandler(this.chatRepository); + } + return this._sendMessageCommandHandler; + } + get archiveChatCommandHandler() { + if (!this._archiveChatCommandHandler) { + this._archiveChatCommandHandler = new ChatArchiveCommandHandlers_1.ArchiveChatCommandHandler(this.chatRepository); + } + return this._archiveChatCommandHandler; + } + get restoreChatCommandHandler() { + if (!this._restoreChatCommandHandler) { + this._restoreChatCommandHandler = new ChatArchiveCommandHandlers_1.RestoreChatCommandHandler(this.chatRepository); + } + return this._restoreChatCommandHandler; + } + get createDeckCommandHandler() { + if (!this._createDeckCommandHandler) { + this._createDeckCommandHandler = new CreateDeckCommandHandler_1.CreateDeckCommandHandler(this.deckRepository, this.userRepository, this.organizationRepository); + } + return this._createDeckCommandHandler; + } + get updateDeckCommandHandler() { + if (!this._updateDeckCommandHandler) { + this._updateDeckCommandHandler = new UpdateDeckCommandHandler_1.UpdateDeckCommandHandler(this.deckRepository); + } + return this._updateDeckCommandHandler; + } + get deleteDeckCommandHandler() { + if (!this._deleteDeckCommandHandler) { + this._deleteDeckCommandHandler = new DeleteDeckCommandHandler_1.DeleteDeckCommandHandler(this.deckRepository); + } + return this._deleteDeckCommandHandler; + } + get createOrganizationCommandHandler() { + if (!this._createOrganizationCommandHandler) { + this._createOrganizationCommandHandler = new CreateOrganizationCommandHandler_1.CreateOrganizationCommandHandler(this.organizationRepository); + } + return this._createOrganizationCommandHandler; + } + get updateOrganizationCommandHandler() { + if (!this._updateOrganizationCommandHandler) { + this._updateOrganizationCommandHandler = new UpdateOrganizationCommandHandler_1.UpdateOrganizationCommandHandler(this.organizationRepository); + } + return this._updateOrganizationCommandHandler; + } + get deleteOrganizationCommandHandler() { + if (!this._deleteOrganizationCommandHandler) { + this._deleteOrganizationCommandHandler = new DeleteOrganizationCommandHandler_1.DeleteOrganizationCommandHandler(this.organizationRepository); + } + return this._deleteOrganizationCommandHandler; + } + get processOrgAuthCallbackCommandHandler() { + if (!this._processOrgAuthCallbackCommandHandler) { + this._processOrgAuthCallbackCommandHandler = new ProcessOrgAuthCallbackCommandHandler_1.ProcessOrgAuthCallbackCommandHandler(this.userRepository, this.organizationRepository); + } + return this._processOrgAuthCallbackCommandHandler; + } + get createContactCommandHandler() { + if (!this._createContactCommandHandler) { + this._createContactCommandHandler = new CreateContactCommandHandler_1.CreateContactCommandHandler(this.contactRepository); + } + return this._createContactCommandHandler; + } + get updateContactCommandHandler() { + if (!this._updateContactCommandHandler) { + this._updateContactCommandHandler = new UpdateContactCommandHandler_1.UpdateContactCommandHandler(this.contactRepository); + } + return this._updateContactCommandHandler; + } + get deleteContactCommandHandler() { + if (!this._deleteContactCommandHandler) { + this._deleteContactCommandHandler = new DeleteContactCommandHandler_1.DeleteContactCommandHandler(this.contactRepository); + } + return this._deleteContactCommandHandler; + } + // Query Handler getters + get getUserByIdQueryHandler() { + if (!this._getUserByIdQueryHandler) { + this._getUserByIdQueryHandler = new GetUserByIdQueryHandler_1.GetUserByIdQueryHandler(this.userRepository); + } + return this._getUserByIdQueryHandler; + } + get getUserChatsQueryHandler() { + if (!this._getUserChatsQueryHandler) { + this._getUserChatsQueryHandler = new GetUserChatsQueryHandler_1.GetUserChatsQueryHandler(this.chatRepository, this.chatArchiveRepository); + } + return this._getUserChatsQueryHandler; + } + get getChatHistoryQueryHandler() { + if (!this._getChatHistoryQueryHandler) { + this._getChatHistoryQueryHandler = new ChatHistoryQueryHandlers_1.GetChatHistoryQueryHandler(this.chatRepository, this.chatArchiveRepository); + } + return this._getChatHistoryQueryHandler; + } + get getArchivedChatsQueryHandler() { + if (!this._getArchivedChatsQueryHandler) { + this._getArchivedChatsQueryHandler = new ChatHistoryQueryHandlers_1.GetArchivedChatsQueryHandler(this.chatArchiveRepository); + } + return this._getArchivedChatsQueryHandler; + } + get getDeckByIdQueryHandler() { + if (!this._getDeckByIdQueryHandler) { + this._getDeckByIdQueryHandler = new GetDeckByIdQueryHandler_1.GetDeckByIdQueryHandler(this.deckRepository); + } + return this._getDeckByIdQueryHandler; + } + get getOrganizationByIdQueryHandler() { + if (!this._getOrganizationByIdQueryHandler) { + this._getOrganizationByIdQueryHandler = new GetOrganizationByIdQueryHandler_1.GetOrganizationByIdQueryHandler(this.organizationRepository); + } + return this._getOrganizationByIdQueryHandler; + } + get getOrganizationLoginUrlQueryHandler() { + if (!this._getOrganizationLoginUrlQueryHandler) { + this._getOrganizationLoginUrlQueryHandler = new GetOrganizationLoginUrlQueryHandler_1.GetOrganizationLoginUrlQueryHandler(this.organizationRepository); + } + return this._getOrganizationLoginUrlQueryHandler; + } + get getContactByIdQueryHandler() { + if (!this._getContactByIdQueryHandler) { + this._getContactByIdQueryHandler = new GetContactByIdQueryHandler_1.GetContactByIdQueryHandler(this.contactRepository); + } + return this._getContactByIdQueryHandler; + } + get getContactsByPageQueryHandler() { + if (!this._getContactsByPageQueryHandler) { + this._getContactsByPageQueryHandler = new GetContactsByPageQueryHandler_1.GetContactsByPageQueryHandler(this.contactRepository); + } + return this._getContactsByPageQueryHandler; + } + // New paginated query handlers + get getUsersByPageQueryHandler() { + if (!this._getUsersByPageQueryHandler) { + this._getUsersByPageQueryHandler = new GetUsersByPageQueryHandler_1.GetUsersByPageQueryHandler(this.userRepository); + } + return this._getUsersByPageQueryHandler; + } + get getDecksByPageQueryHandler() { + if (!this._getDecksByPageQueryHandler) { + this._getDecksByPageQueryHandler = new GetDecksByPageQueryHandler_1.GetDecksByPageQueryHandler(this.deckRepository); + } + return this._getDecksByPageQueryHandler; + } + get getOrganizationsByPageQueryHandler() { + if (!this._getOrganizationsByPageQueryHandler) { + this._getOrganizationsByPageQueryHandler = new GetOrganizationsByPageQueryHandler_1.GetOrganizationsByPageQueryHandler(this.organizationRepository); + } + return this._getOrganizationsByPageQueryHandler; + } + get getChatsByPageQueryHandler() { + if (!this._getChatsByPageQueryHandler) { + this._getChatsByPageQueryHandler = new GetChatsByPageQueryHandler_1.GetChatsByPageQueryHandler(this.chatRepository); + } + return this._getChatsByPageQueryHandler; + } +} +exports.DIContainer = DIContainer; +// Export singleton instance +exports.container = DIContainer.getInstance(); +//# sourceMappingURL=DIContainer.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DIContainer.js.map b/SerpentRace_Backend/dist/Application/Services/DIContainer.js.map new file mode 100644 index 00000000..1eb30976 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DIContainer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DIContainer.js","sourceRoot":"","sources":["../../../src/Application/Services/DIContainer.ts"],"names":[],"mappings":";;;AAQA,6BAA6B;AAC7B,mFAAgF;AAChF,mFAAgF;AAChF,iGAA8F;AAC9F,mFAAgF;AAChF,mGAAgG;AAChG,yFAAsF;AAEtF,mBAAmB;AACnB,wFAAqF;AACrF,8EAA2E;AAC3E,wFAAqF;AACrF,gGAA6F;AAC7F,wFAAqF;AACrF,0FAAuF;AACvF,4GAAyG;AACzG,8FAA2F;AAC3F,wFAAqF;AACrF,0FAAuF;AACvF,4FAAmH;AACnH,wFAAqF;AACrF,wFAAqF;AACrF,wFAAqF;AACrF,gHAA6G;AAC7G,gHAA6G;AAC7G,gHAA6G;AAC7G,wHAAqH;AACrH,iGAA8F;AAC9F,iGAA8F;AAC9F,iGAA8F;AAE9F,iBAAiB;AACjB,qFAAkF;AAClF,2FAAwF;AACxF,uFAAoF;AACpF,uFAAoH;AACpH,2FAAwF;AACxF,qFAAkF;AAClF,2FAAwF;AACxF,6GAA0G;AAC1G,mHAAgH;AAChH,qHAAkH;AAClH,8FAA2F;AAC3F,oGAAiG;AAEjG,WAAW;AACX,6CAA0C;AAC1C,+DAA4D;AAC5D,uEAAoE;AAEpE;;;GAGG;AACH,MAAa,WAAW;IAuDpB;QApDA,yDAAyD;QACjD,oBAAe,GAA2B,IAAI,CAAC;QAC/C,oBAAe,GAA2B,IAAI,CAAC;QAC/C,2BAAsB,GAAkC,IAAI,CAAC;QAC7D,oBAAe,GAA2B,IAAI,CAAC;QAC/C,4BAAuB,GAAmC,IAAI,CAAC;QAC/D,uBAAkB,GAA8B,IAAI,CAAC;QAE7D,WAAW;QACH,gBAAW,GAAsB,IAAI,CAAC;QACtC,yBAAoB,GAA+B,IAAI,CAAC;QACxD,6BAAwB,GAAmC,IAAI,CAAC;QAExE,mBAAmB;QACX,8BAAyB,GAAoC,IAAI,CAAC;QAClE,yBAAoB,GAA+B,IAAI,CAAC;QACxD,8BAAyB,GAAoC,IAAI,CAAC;QAClE,kCAA6B,GAAwC,IAAI,CAAC;QAC1E,8BAAyB,GAAoC,IAAI,CAAC;QAClE,+BAA0B,GAAqC,IAAI,CAAC;QACpE,wCAAmC,GAA8C,IAAI,CAAC;QACtF,iCAA4B,GAAuC,IAAI,CAAC;QACxE,8BAAyB,GAAoC,IAAI,CAAC;QAClE,+BAA0B,GAAqC,IAAI,CAAC;QACpE,+BAA0B,GAAqC,IAAI,CAAC;QACpE,+BAA0B,GAAqC,IAAI,CAAC;QACpE,8BAAyB,GAAoC,IAAI,CAAC;QAClE,8BAAyB,GAAoC,IAAI,CAAC;QAClE,8BAAyB,GAAoC,IAAI,CAAC;QAClE,sCAAiC,GAA4C,IAAI,CAAC;QAClF,sCAAiC,GAA4C,IAAI,CAAC;QAClF,sCAAiC,GAA4C,IAAI,CAAC;QAClF,0CAAqC,GAAgD,IAAI,CAAC;QAC1F,iCAA4B,GAAuC,IAAI,CAAC;QACxE,iCAA4B,GAAuC,IAAI,CAAC;QACxE,iCAA4B,GAAuC,IAAI,CAAC;QAEhF,iBAAiB;QACT,6BAAwB,GAAmC,IAAI,CAAC;QAChE,gCAA2B,GAAsC,IAAI,CAAC;QACtE,8BAAyB,GAAoC,IAAI,CAAC;QAClE,gCAA2B,GAAsC,IAAI,CAAC;QACtE,kCAA6B,GAAwC,IAAI,CAAC;QAC1E,gCAA2B,GAAsC,IAAI,CAAC;QACtE,6BAAwB,GAAmC,IAAI,CAAC;QAChE,gCAA2B,GAAsC,IAAI,CAAC;QACtE,qCAAgC,GAA2C,IAAI,CAAC;QAChF,wCAAmC,GAA8C,IAAI,CAAC;QACtF,yCAAoC,GAA+C,IAAI,CAAC;QACxF,gCAA2B,GAAsC,IAAI,CAAC;QACtE,mCAA8B,GAAyC,IAAI,CAAC;IAE7D,CAAC;IAEjB,MAAM,CAAC,WAAW;QACrB,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;YACxB,WAAW,CAAC,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,WAAW,CAAC,QAAQ,CAAC;IAChC,CAAC;IAED,gEAAgE;IAChE,IAAW,cAAc;QACrB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YACxB,IAAI,CAAC,eAAe,GAAG,IAAI,+BAAc,EAAE,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED,IAAW,cAAc;QACrB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YACxB,IAAI,CAAC,eAAe,GAAG,IAAI,+BAAc,EAAE,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED,IAAW,qBAAqB;QAC5B,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC/B,IAAI,CAAC,sBAAsB,GAAG,IAAI,6CAAqB,EAAE,CAAC;QAC9D,CAAC;QACD,OAAO,IAAI,CAAC,sBAAsB,CAAC;IACvC,CAAC;IAED,IAAW,cAAc;QACrB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YACxB,IAAI,CAAC,eAAe,GAAG,IAAI,+BAAc,EAAE,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED,IAAW,sBAAsB;QAC7B,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAChC,IAAI,CAAC,uBAAuB,GAAG,IAAI,+CAAsB,EAAE,CAAC;QAChE,CAAC;QACD,OAAO,IAAI,CAAC,uBAAuB,CAAC;IACxC,CAAC;IAED,IAAW,iBAAiB;QACxB,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC3B,IAAI,CAAC,kBAAkB,GAAG,IAAI,qCAAiB,EAAE,CAAC;QACtD,CAAC;QACD,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACnC,CAAC;IAED,mBAAmB;IACnB,IAAW,UAAU;QACjB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,IAAI,CAAC,WAAW,GAAG,IAAI,uBAAU,EAAE,CAAC;QACxC,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,IAAW,mBAAmB;QAC1B,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC7B,IAAI,CAAC,oBAAoB,GAAG,IAAI,yCAAmB,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACrC,CAAC;IAED,IAAW,uBAAuB;QAC9B,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;YACjC,IAAI,CAAC,wBAAwB,GAAG,IAAI,iDAAuB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,IAAI,CAAC,wBAAwB,CAAC;IACzC,CAAC;IAED,0BAA0B;IAC1B,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,mBAAmB;QAC1B,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC7B,IAAI,CAAC,oBAAoB,GAAG,IAAI,yCAAmB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC3H,CAAC;QACD,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACrC,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,4BAA4B;QACnC,IAAI,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC;YACtC,IAAI,CAAC,6BAA6B,GAAG,IAAI,2DAA4B,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/F,CAAC;QACD,OAAO,IAAI,CAAC,6BAA6B,CAAC;IAC9C,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,yBAAyB;QAChC,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC;YACnC,IAAI,CAAC,0BAA0B,GAAG,IAAI,qDAAyB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,IAAI,CAAC,0BAA0B,CAAC;IAC3C,CAAC;IAED,IAAW,kCAAkC;QACzC,IAAI,CAAC,IAAI,CAAC,mCAAmC,EAAE,CAAC;YAC5C,IAAI,CAAC,mCAAmC,GAAG,IAAI,uEAAkC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,IAAI,CAAC,mCAAmC,CAAC;IACpD,CAAC;IAED,IAAW,2BAA2B;QAClC,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACrC,IAAI,CAAC,4BAA4B,GAAG,IAAI,yDAA2B,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7F,CAAC;QACD,OAAO,IAAI,CAAC,4BAA4B,CAAC;IAC7C,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAC5G,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,yBAAyB;QAChC,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC;YACnC,IAAI,CAAC,0BAA0B,GAAG,IAAI,qDAAyB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,IAAI,CAAC,0BAA0B,CAAC;IAC3C,CAAC;IAED,IAAW,yBAAyB;QAChC,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC;YACnC,IAAI,CAAC,0BAA0B,GAAG,IAAI,sDAAyB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,IAAI,CAAC,0BAA0B,CAAC;IAC3C,CAAC;IAED,IAAW,yBAAyB;QAChC,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC;YACnC,IAAI,CAAC,0BAA0B,GAAG,IAAI,sDAAyB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,IAAI,CAAC,0BAA0B,CAAC;IAC3C,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CACzD,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,sBAAsB,CAC9B,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,gCAAgC;QACvC,IAAI,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC;YAC1C,IAAI,CAAC,iCAAiC,GAAG,IAAI,mEAAgC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC/G,CAAC;QACD,OAAO,IAAI,CAAC,iCAAiC,CAAC;IAClD,CAAC;IAED,IAAW,gCAAgC;QACvC,IAAI,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC;YAC1C,IAAI,CAAC,iCAAiC,GAAG,IAAI,mEAAgC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC/G,CAAC;QACD,OAAO,IAAI,CAAC,iCAAiC,CAAC;IAClD,CAAC;IAED,IAAW,gCAAgC;QACvC,IAAI,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC;YAC1C,IAAI,CAAC,iCAAiC,GAAG,IAAI,mEAAgC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC/G,CAAC;QACD,OAAO,IAAI,CAAC,iCAAiC,CAAC;IAClD,CAAC;IAED,IAAW,oCAAoC;QAC3C,IAAI,CAAC,IAAI,CAAC,qCAAqC,EAAE,CAAC;YAC9C,IAAI,CAAC,qCAAqC,GAAG,IAAI,2EAAoC,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC5I,CAAC;QACD,OAAO,IAAI,CAAC,qCAAqC,CAAC;IACtD,CAAC;IAED,IAAW,2BAA2B;QAClC,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACrC,IAAI,CAAC,4BAA4B,GAAG,IAAI,yDAA2B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,IAAI,CAAC,4BAA4B,CAAC;IAC7C,CAAC;IAED,IAAW,2BAA2B;QAClC,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACrC,IAAI,CAAC,4BAA4B,GAAG,IAAI,yDAA2B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,IAAI,CAAC,4BAA4B,CAAC;IAC7C,CAAC;IAED,IAAW,2BAA2B;QAClC,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACrC,IAAI,CAAC,4BAA4B,GAAG,IAAI,yDAA2B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,IAAI,CAAC,4BAA4B,CAAC;IAC7C,CAAC;IAED,wBAAwB;IACxB,IAAW,uBAAuB;QAC9B,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;YACjC,IAAI,CAAC,wBAAwB,GAAG,IAAI,iDAAuB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,IAAI,CAAC,wBAAwB,CAAC;IACzC,CAAC;IAED,IAAW,wBAAwB;QAC/B,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAClC,IAAI,CAAC,yBAAyB,GAAG,IAAI,mDAAwB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACnH,CAAC;QACD,OAAO,IAAI,CAAC,yBAAyB,CAAC;IAC1C,CAAC;IAED,IAAW,0BAA0B;QACjC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;YACpC,IAAI,CAAC,2BAA2B,GAAG,IAAI,qDAA0B,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACvH,CAAC;QACD,OAAO,IAAI,CAAC,2BAA2B,CAAC;IAC5C,CAAC;IAED,IAAW,4BAA4B;QACnC,IAAI,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC;YACtC,IAAI,CAAC,6BAA6B,GAAG,IAAI,uDAA4B,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACtG,CAAC;QACD,OAAO,IAAI,CAAC,6BAA6B,CAAC;IAC9C,CAAC;IAED,IAAW,uBAAuB;QAC9B,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;YACjC,IAAI,CAAC,wBAAwB,GAAG,IAAI,iDAAuB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,IAAI,CAAC,wBAAwB,CAAC;IACzC,CAAC;IAED,IAAW,+BAA+B;QACtC,IAAI,CAAC,IAAI,CAAC,gCAAgC,EAAE,CAAC;YACzC,IAAI,CAAC,gCAAgC,GAAG,IAAI,iEAA+B,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC7G,CAAC;QACD,OAAO,IAAI,CAAC,gCAAgC,CAAC;IACjD,CAAC;IAED,IAAW,mCAAmC;QAC1C,IAAI,CAAC,IAAI,CAAC,oCAAoC,EAAE,CAAC;YAC7C,IAAI,CAAC,oCAAoC,GAAG,IAAI,yEAAmC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACrH,CAAC;QACD,OAAO,IAAI,CAAC,oCAAoC,CAAC;IACrD,CAAC;IAED,IAAW,0BAA0B;QACjC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;YACpC,IAAI,CAAC,2BAA2B,GAAG,IAAI,uDAA0B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,IAAI,CAAC,2BAA2B,CAAC;IAC5C,CAAC;IAED,IAAW,6BAA6B;QACpC,IAAI,CAAC,IAAI,CAAC,8BAA8B,EAAE,CAAC;YACvC,IAAI,CAAC,8BAA8B,GAAG,IAAI,6DAA6B,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACpG,CAAC;QACD,OAAO,IAAI,CAAC,8BAA8B,CAAC;IAC/C,CAAC;IAED,+BAA+B;IAC/B,IAAW,0BAA0B;QACjC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;YACpC,IAAI,CAAC,2BAA2B,GAAG,IAAI,uDAA0B,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,2BAA2B,CAAC;IAC5C,CAAC;IAED,IAAW,0BAA0B;QACjC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;YACpC,IAAI,CAAC,2BAA2B,GAAG,IAAI,uDAA0B,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,2BAA2B,CAAC;IAC5C,CAAC;IAED,IAAW,kCAAkC;QACzC,IAAI,CAAC,IAAI,CAAC,mCAAmC,EAAE,CAAC;YAC5C,IAAI,CAAC,mCAAmC,GAAG,IAAI,uEAAkC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACnH,CAAC;QACD,OAAO,IAAI,CAAC,mCAAmC,CAAC;IACpD,CAAC;IAED,IAAW,0BAA0B;QACjC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;YACpC,IAAI,CAAC,2BAA2B,GAAG,IAAI,uDAA0B,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,2BAA2B,CAAC;IAC5C,CAAC;CACJ;AA5XD,kCA4XC;AAED,4BAA4B;AACf,QAAA,SAAS,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.d.ts b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.d.ts new file mode 100644 index 00000000..4f27a234 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.d.ts @@ -0,0 +1,31 @@ +import { DeckAggregate } from '../../Domain/Deck/DeckAggregate'; +import { IDeckRepository } from '../../Domain/IRepository/IDeckRepository'; +export interface SprDeckData { + name: string; + type: number; + cards: any[]; + ctype: number; + exportDate: string; + version: string; +} +export interface ImportDeckCommand { + name: string; + type: number; + cards: any[]; + ctype?: number; + userid: string; +} +export declare class DeckImportExportService { + private readonly deckRepo; + private readonly encryptionKey; + private readonly algorithm; + constructor(deckRepo: IDeckRepository); + exportDeckToSpr(deckId: string, userId: string): Promise; + importDeckFromSpr(sprData: Buffer, userId: string): Promise; + importDeckFromJson(jsonData: any, userId: string): Promise; + adminImportFromJson(jsonData: any, targetUserId: string, adminUserId: string): Promise; + private encrypt; + private decrypt; + generateFilename(deckName: string): string; +} +//# sourceMappingURL=DeckImportExportService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.d.ts.map new file mode 100644 index 00000000..9265a7ed --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckImportExportService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/DeckImportExportService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAgB,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAG3E,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,uBAAuB;IAIpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAHrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;gBAEd,QAAQ,EAAE,eAAe;IAQhD,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqChE,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAmC1E,kBAAkB,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAgCzE,mBAAmB,CAAC,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA8B3G,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,OAAO;IAmBf,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAM7C"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.js b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.js new file mode 100644 index 00000000..2703a50f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.js @@ -0,0 +1,195 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeckImportExportService = void 0; +const crypto = __importStar(require("crypto")); +const DeckAggregate_1 = require("../../Domain/Deck/DeckAggregate"); +const Logger_1 = require("./Logger"); +class DeckImportExportService { + constructor(deckRepo) { + this.deckRepo = deckRepo; + this.algorithm = 'aes-256-gcm'; + this.encryptionKey = process.env.DECK_ENCRYPTION_KEY || 'your-32-byte-encryption-key-here!!'; + if (this.encryptionKey.length !== 32) { + throw new Error('DECK_ENCRYPTION_KEY must be exactly 32 characters long'); + } + } + async exportDeckToSpr(deckId, userId) { + try { + const deck = await this.deckRepo.findByIdIncludingDeleted(deckId); + if (!deck) { + throw new Error('Deck not found'); + } + if (deck.userid !== userId) { + throw new Error('Unauthorized: You can only export your own decks'); + } + const deckData = { + name: deck.name, + type: deck.type, + cards: deck.cards, + ctype: deck.ctype, + exportDate: new Date().toISOString(), + version: '1.0' + }; + const jsonString = JSON.stringify(deckData); + const encrypted = this.encrypt(jsonString); + (0, Logger_1.logAuth)('Deck exported to SPR format', userId, { + deckId: deck.id, + deckName: deck.name, + cardCount: deck.cards.length + }); + return encrypted; + } + catch (error) { + (0, Logger_1.logError)('Failed to export deck to SPR', error); + throw error; + } + } + async importDeckFromSpr(sprData, userId) { + try { + const decrypted = this.decrypt(sprData); + const deckData = JSON.parse(decrypted); + // Validate required fields + if (!deckData.name || !deckData.cards || deckData.type === undefined) { + throw new Error('Invalid SPR file format: missing required fields'); + } + // Create new deck + const newDeck = new DeckAggregate_1.DeckAggregate(); + newDeck.name = deckData.name; + newDeck.type = deckData.type; + newDeck.userid = userId; + newDeck.cards = deckData.cards; + newDeck.ctype = deckData.ctype || DeckAggregate_1.CType.PUBLIC; + newDeck.state = DeckAggregate_1.State.ACTIVE; + const createdDeck = await this.deckRepo.create(newDeck); + (0, Logger_1.logAuth)('Deck imported from SPR format', userId, { + deckId: createdDeck.id, + deckName: createdDeck.name, + cardCount: createdDeck.cards.length, + originalExportDate: deckData.exportDate + }); + return createdDeck; + } + catch (error) { + (0, Logger_1.logError)('Failed to import deck from SPR', error); + throw error; + } + } + async importDeckFromJson(jsonData, userId) { + try { + // Validate required fields + if (!jsonData.name || !jsonData.cards || jsonData.type === undefined) { + throw new Error('Invalid JSON format: missing required fields (name, cards, type)'); + } + // Create new deck + const newDeck = new DeckAggregate_1.DeckAggregate(); + newDeck.name = jsonData.name; + newDeck.type = jsonData.type; + newDeck.userid = userId; + newDeck.cards = jsonData.cards; + newDeck.ctype = jsonData.ctype || DeckAggregate_1.CType.PUBLIC; + newDeck.state = DeckAggregate_1.State.ACTIVE; + const createdDeck = await this.deckRepo.create(newDeck); + (0, Logger_1.logAuth)('Deck imported from JSON format', userId, { + deckId: createdDeck.id, + deckName: createdDeck.name, + cardCount: createdDeck.cards.length + }); + return createdDeck; + } + catch (error) { + (0, Logger_1.logError)('Failed to import deck from JSON', error); + throw error; + } + } + // Admin-only function to import JSON without encryption + async adminImportFromJson(jsonData, targetUserId, adminUserId) { + try { + if (!jsonData.name || !jsonData.cards || jsonData.type === undefined) { + throw new Error('Invalid JSON format: missing required fields (name, cards, type)'); + } + const newDeck = new DeckAggregate_1.DeckAggregate(); + newDeck.name = jsonData.name; + newDeck.type = jsonData.type; + newDeck.userid = targetUserId; + newDeck.cards = jsonData.cards; + newDeck.ctype = jsonData.ctype || DeckAggregate_1.CType.PUBLIC; + newDeck.state = jsonData.state || DeckAggregate_1.State.ACTIVE; + const createdDeck = await this.deckRepo.create(newDeck); + (0, Logger_1.logAuth)('Deck imported by admin from JSON', adminUserId, { + deckId: createdDeck.id, + deckName: createdDeck.name, + cardCount: createdDeck.cards.length, + targetUserId: targetUserId + }); + return createdDeck; + } + catch (error) { + (0, Logger_1.logError)('Failed to admin import deck from JSON', error); + throw error; + } + } + encrypt(text) { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(this.algorithm, this.encryptionKey, iv); + cipher.setAAD(Buffer.from('SerpentRace-Deck', 'utf8')); + let encrypted = cipher.update(text, 'utf8'); + encrypted = Buffer.concat([encrypted, cipher.final()]); + const authTag = cipher.getAuthTag(); + return Buffer.concat([iv, authTag, encrypted]); + } + decrypt(encryptedData) { + if (encryptedData.length < 32) { + throw new Error('Invalid SPR file: file too short'); + } + const iv = encryptedData.slice(0, 16); + const authTag = encryptedData.slice(16, 32); + const encrypted = encryptedData.slice(32); + const decipher = crypto.createDecipheriv(this.algorithm, this.encryptionKey, iv); + decipher.setAAD(Buffer.from('SerpentRace-Deck', 'utf8')); + decipher.setAuthTag(authTag); + let decrypted = decipher.update(encrypted, undefined, 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; + } + generateFilename(deckName) { + // Sanitize deck name for filename + const sanitized = deckName.replace(/[^a-zA-Z0-9\-_]/g, '_'); + const timestamp = new Date().toISOString().split('T')[0]; // YYYY-MM-DD + return `${sanitized}_${timestamp}.spr`; + } +} +exports.DeckImportExportService = DeckImportExportService; +//# sourceMappingURL=DeckImportExportService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.js.map b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.js.map new file mode 100644 index 00000000..88597245 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/DeckImportExportService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckImportExportService.js","sourceRoot":"","sources":["../../../src/Application/Services/DeckImportExportService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailService.d.ts b/SerpentRace_Backend/dist/Application/Services/EmailService.d.ts new file mode 100644 index 00000000..26c057bd --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailService.d.ts @@ -0,0 +1,65 @@ +export interface EmailOptions { + to: string; + subject: string; + html?: string; + text?: string; + template?: string; + templateData?: any; +} +export interface EmailConfig { + host: string; + port: number; + secure: boolean; + auth: { + user: string; + pass: string; + }; + from: string; +} +export declare class EmailService { + private transporter; + private config; + private templatesPath; + constructor(); + private initializeTransporter; + /** + * Send email with template + * @param options - Email options including template and data + */ + sendEmail(options: EmailOptions): Promise; + /** + * Send verification email to user + * @param userEmail - User's email address + * @param userName - User's name + * @param verificationToken - Verification token + * @param verificationUrl - Complete verification URL + * @param language - Language code ('en', 'hu', 'de') + */ + sendVerificationEmail(userEmail: string, userName: string, verificationToken: string, verificationUrl: string, language?: 'en' | 'hu' | 'de'): Promise; + /** + * Send password reset email + * @param userEmail - User's email address + * @param userName - User's name + * @param resetToken - Password reset token + * @param resetUrl - Complete password reset URL + * @param language - Language code ('en', 'hu', 'de') + */ + sendPasswordResetEmail(userEmail: string, userName: string, resetToken: string, resetUrl: string, language?: 'en' | 'hu' | 'de'): Promise; + /** + * Load and compile email template with language support + * @param templateName - Name of the template file (with or without language suffix) + * @param data - Data to replace placeholders in the template + */ + private loadTemplate; + /** + * Get localized verification email subject + * @param language - Language code ('en', 'hu', 'de') + */ + private getLocalizedVerificationSubject; + /** + * Get localized password reset email subject + * @param language - Language code ('en', 'hu', 'de') + */ + private getLocalizedPasswordResetSubject; +} +//# sourceMappingURL=EmailService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/EmailService.d.ts.map new file mode 100644 index 00000000..8e160697 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EmailService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/EmailService.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAAS;;IAmB9B,OAAO,CAAC,qBAAqB;IAiB7B;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAgCxD;;;;;;;OAOG;IACG,qBAAqB,CACzB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,QAAQ,GAAE,IAAI,GAAG,IAAI,GAAG,IAAW,GAClC,OAAO,CAAC,OAAO,CAAC;IAuBnB;;;;;;;OAOG;IACG,sBAAsB,CAC1B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,IAAI,GAAG,IAAI,GAAG,IAAW,GAClC,OAAO,CAAC,OAAO,CAAC;IAuBnB;;;;OAIG;YACW,YAAY;IAsD1B;;;OAGG;IACH,OAAO,CAAC,+BAA+B;IAWvC;;;OAGG;IACH,OAAO,CAAC,gCAAgC;CAUzC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailService.js b/SerpentRace_Backend/dist/Application/Services/EmailService.js new file mode 100644 index 00000000..679ab527 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailService.js @@ -0,0 +1,249 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmailService = void 0; +const nodemailer = __importStar(require("nodemailer")); +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const Logger_1 = require("./Logger"); +const EmailTemplateHelper_1 = require("./EmailTemplateHelper"); +class EmailService { + constructor() { + this.templatesPath = path.join(__dirname, '../../Templates'); + this.config = { + host: process.env.EMAIL_HOST || 'smtp.gmail.com', + port: parseInt(process.env.EMAIL_PORT || '587'), + secure: process.env.EMAIL_SECURE === 'true', + auth: { + user: process.env.EMAIL_USER || '', + pass: process.env.EMAIL_PASS || '' + }, + from: process.env.EMAIL_FROM || 'noreply@serpentrace.com' + }; + this.initializeTransporter(); + } + initializeTransporter() { + try { + this.transporter = nodemailer.createTransport({ + host: this.config.host, + port: this.config.port, + secure: this.config.secure, + auth: { + user: this.config.auth.user, + pass: this.config.auth.pass + } + }); + } + catch (error) { + (0, Logger_1.logError)('EmailService initialization failed', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to initialize email service'); + } + } + /** + * Send email with template + * @param options - Email options including template and data + */ + async sendEmail(options) { + try { + let htmlContent = options.html; + let textContent = options.text; + if (options.template) { + const templateResult = await this.loadTemplate(options.template, options.templateData || {}); + htmlContent = templateResult.html; + textContent = templateResult.text; + } + const mailOptions = { + from: this.config.from, + to: options.to, + subject: options.subject, + html: htmlContent, + text: textContent + }; + const result = await this.transporter.sendMail(mailOptions); + (0, Logger_1.logAuth)('Email sent successfully', undefined, { + messageId: result.messageId, + to: options.to, + subject: options.subject + }); + return true; + } + catch (error) { + (0, Logger_1.logError)('Email sending failed', error instanceof Error ? error : new Error(String(error))); + return false; + } + } + /** + * Send verification email to user + * @param userEmail - User's email address + * @param userName - User's name + * @param verificationToken - Verification token + * @param verificationUrl - Complete verification URL + * @param language - Language code ('en', 'hu', 'de') + */ + async sendVerificationEmail(userEmail, userName, verificationToken, verificationUrl, language = 'en') { + try { + const templateName = language === 'en' ? 'verification' : `verification-${language}`; + const subject = this.getLocalizedVerificationSubject(language); + return await this.sendEmail({ + to: userEmail, + subject, + template: templateName, + templateData: { + userName, + verificationToken, + verificationUrl, + companyName: 'SerpentRace', + supportEmail: 'support@serpentrace.com' + } + }); + } + catch (error) { + (0, Logger_1.logError)('Verification email sending failed', error instanceof Error ? error : new Error(String(error))); + return false; + } + } + /** + * Send password reset email + * @param userEmail - User's email address + * @param userName - User's name + * @param resetToken - Password reset token + * @param resetUrl - Complete password reset URL + * @param language - Language code ('en', 'hu', 'de') + */ + async sendPasswordResetEmail(userEmail, userName, resetToken, resetUrl, language = 'en') { + try { + const templateName = language === 'en' ? 'password-reset' : `password-reset-${language}`; + const subject = this.getLocalizedPasswordResetSubject(language); + return await this.sendEmail({ + to: userEmail, + subject, + template: templateName, + templateData: { + userName, + resetToken, + resetUrl, + companyName: 'SerpentRace', + supportEmail: 'support@serpentrace.com' + } + }); + } + catch (error) { + (0, Logger_1.logError)('Password reset email sending failed', error instanceof Error ? error : new Error(String(error))); + return false; + } + } + /** + * Load and compile email template with language support + * @param templateName - Name of the template file (with or without language suffix) + * @param data - Data to replace placeholders in the template + */ + async loadTemplate(templateName, data) { + try { + // Try the specified template first + let htmlTemplatePath = path.join(this.templatesPath, `${templateName}.html`); + let textTemplatePath = path.join(this.templatesPath, `${templateName}.txt`); + let htmlTemplate = ''; + let textTemplate = ''; + // Load HTML template if it exists + if (fs.existsSync(htmlTemplatePath)) { + htmlTemplate = fs.readFileSync(htmlTemplatePath, 'utf8'); + } + else { + // If language-specific template doesn't exist, try fallback to English + const baseName = templateName.replace(/-[a-z]{2}$/, ''); // Remove language suffix + const fallbackHtmlPath = path.join(this.templatesPath, `${baseName}.html`); + if (fs.existsSync(fallbackHtmlPath)) { + htmlTemplate = fs.readFileSync(fallbackHtmlPath, 'utf8'); + } + } + // Load text template if it exists + if (fs.existsSync(textTemplatePath)) { + textTemplate = fs.readFileSync(textTemplatePath, 'utf8'); + } + else { + // If language-specific template doesn't exist, try fallback to English + const baseName = templateName.replace(/-[a-z]{2}$/, ''); // Remove language suffix + const fallbackTextPath = path.join(this.templatesPath, `${baseName}.txt`); + if (fs.existsSync(fallbackTextPath)) { + textTemplate = fs.readFileSync(fallbackTextPath, 'utf8'); + } + } + // If no templates found, throw error + if (!htmlTemplate && !textTemplate) { + throw new Error(`Template '${templateName}' not found`); + } + // Replace placeholders in templates + const processedTemplate = EmailTemplateHelper_1.EmailTemplateHelper.processTemplate({ html: htmlTemplate, text: textTemplate }, data); + return { + html: processedTemplate.html, + text: processedTemplate.text + }; + } + catch (error) { + (0, Logger_1.logError)('Email template loading failed', error instanceof Error ? error : new Error(String(error))); + throw new Error(`Failed to load email template: ${templateName}`); + } + } + /** + * Get localized verification email subject + * @param language - Language code ('en', 'hu', 'de') + */ + getLocalizedVerificationSubject(language) { + const subjects = { + verification: { + en: 'SerpentRace - Verify Your Account', + hu: 'SerpentRace - Fiók megerősítése', + de: 'SerpentRace - Konto verifizieren' + } + }; + return EmailTemplateHelper_1.EmailTemplateHelper.getLocalizedSubject('verification', subjects, language); + } + /** + * Get localized password reset email subject + * @param language - Language code ('en', 'hu', 'de') + */ + getLocalizedPasswordResetSubject(language) { + const subjects = { + passwordReset: { + en: 'SerpentRace - Password Reset Request', + hu: 'SerpentRace - Jelszó visszaállítás kérése', + de: 'SerpentRace - Passwort zurücksetzen' + } + }; + return EmailTemplateHelper_1.EmailTemplateHelper.getLocalizedSubject('passwordReset', subjects, language); + } +} +exports.EmailService = EmailService; +//# sourceMappingURL=EmailService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailService.js.map b/SerpentRace_Backend/dist/Application/Services/EmailService.js.map new file mode 100644 index 00000000..ae48a3b2 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EmailService.js","sourceRoot":"","sources":["../../../src/Application/Services/EmailService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.d.ts b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.d.ts new file mode 100644 index 00000000..21d70724 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.d.ts @@ -0,0 +1,20 @@ +export interface LocalizedSubjects { + [key: string]: { + en: string; + hu: string; + de: string; + }; +} +export interface TemplateData { + [key: string]: any; +} +export interface EmailTemplate { + html: string; + text: string; +} +export declare class EmailTemplateHelper { + static getLocalizedSubject(subjectKey: string, subjects: LocalizedSubjects, language: 'en' | 'hu' | 'de'): string; + static replaceTemplatePlaceholders(template: string, data: TemplateData): string; + static processTemplate(templateContent: EmailTemplate, data: TemplateData): EmailTemplate; +} +//# sourceMappingURL=EmailTemplateHelper.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.d.ts.map b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.d.ts.map new file mode 100644 index 00000000..3657ffce --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EmailTemplateHelper.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/EmailTemplateHelper.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG;QACb,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,mBAAmB;WAChB,mBAAmB,CAC/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAC3B,MAAM;WAIK,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM;WAMzE,eAAe,CAAC,eAAe,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,aAAa;CAMjG"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.js b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.js new file mode 100644 index 00000000..755d33d1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmailTemplateHelper = void 0; +class EmailTemplateHelper { + static getLocalizedSubject(subjectKey, subjects, language) { + return subjects[subjectKey]?.[language] || subjects[subjectKey]?.['en'] || 'SerpentRace'; + } + static replaceTemplatePlaceholders(template, data) { + return template.replace(/\{\{(\w+)\}\}/g, (match, key) => { + return data[key] !== undefined ? String(data[key]) : match; + }); + } + static processTemplate(templateContent, data) { + return { + html: this.replaceTemplatePlaceholders(templateContent.html, data), + text: this.replaceTemplatePlaceholders(templateContent.text, data) + }; + } +} +exports.EmailTemplateHelper = EmailTemplateHelper; +//# sourceMappingURL=EmailTemplateHelper.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.js.map b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.js.map new file mode 100644 index 00000000..dac86041 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/EmailTemplateHelper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EmailTemplateHelper.js","sourceRoot":"","sources":["../../../src/Application/Services/EmailTemplateHelper.ts"],"names":[],"mappings":";;;AAiBA,MAAa,mBAAmB;IACvB,MAAM,CAAC,mBAAmB,CAC/B,UAAkB,EAClB,QAA2B,EAC3B,QAA4B;QAE5B,OAAO,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC;IAC3F,CAAC;IAEM,MAAM,CAAC,2BAA2B,CAAC,QAAgB,EAAE,IAAkB;QAC5E,OAAO,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACvD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,MAAM,CAAC,eAAe,CAAC,eAA8B,EAAE,IAAkB;QAC9E,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,2BAA2B,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC;YAClE,IAAI,EAAE,IAAI,CAAC,2BAA2B,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC;SACnE,CAAC;IACJ,CAAC;CACF;AArBD,kDAqBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.d.ts b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.d.ts new file mode 100644 index 00000000..55252ca6 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.d.ts @@ -0,0 +1,11 @@ +import { Response } from 'express'; +export declare class ErrorResponseService { + static sendError(res: Response, statusCode: number, message: string, details?: any): Response; + static sendInternalServerError(res: Response): Response; + static sendBadRequest(res: Response, message?: string, details?: any): Response; + static sendUnauthorized(res: Response, message?: string): Response; + static sendForbidden(res: Response, message?: string): Response; + static sendNotFound(res: Response, message?: string): Response; + static sendConflict(res: Response, message?: string): Response; +} +//# sourceMappingURL=ErrorResponseService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.d.ts.map new file mode 100644 index 00000000..1d9d1ff9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ErrorResponseService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/ErrorResponseService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,qBAAa,oBAAoB;IAC/B,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,QAAQ;IAQ7F,MAAM,CAAC,uBAAuB,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ;IAIvD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,MAAsB,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,QAAQ;IAI9F,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,MAAuB,GAAG,QAAQ;IAIlF,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,MAAoB,GAAG,QAAQ;IAI5E,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,MAAoB,GAAG,QAAQ;IAI3E,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,MAAmB,GAAG,QAAQ;CAG3E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.js b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.js new file mode 100644 index 00000000..8428da02 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ErrorResponseService = void 0; +class ErrorResponseService { + static sendError(res, statusCode, message, details) { + const errorResponse = { error: message }; + if (details) { + errorResponse.details = details; + } + return res.status(statusCode).json(errorResponse); + } + static sendInternalServerError(res) { + return this.sendError(res, 500, 'Internal server error'); + } + static sendBadRequest(res, message = 'Bad request', details) { + return this.sendError(res, 400, message, details); + } + static sendUnauthorized(res, message = 'Unauthorized') { + return this.sendError(res, 401, message); + } + static sendForbidden(res, message = 'Forbidden') { + return this.sendError(res, 403, message); + } + static sendNotFound(res, message = 'Not found') { + return this.sendError(res, 404, message); + } + static sendConflict(res, message = 'Conflict') { + return this.sendError(res, 409, message); + } +} +exports.ErrorResponseService = ErrorResponseService; +//# sourceMappingURL=ErrorResponseService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.js.map b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.js.map new file mode 100644 index 00000000..19feef9d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ErrorResponseService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ErrorResponseService.js","sourceRoot":"","sources":["../../../src/Application/Services/ErrorResponseService.ts"],"names":[],"mappings":";;;AAEA,MAAa,oBAAoB;IAC/B,MAAM,CAAC,SAAS,CAAC,GAAa,EAAE,UAAkB,EAAE,OAAe,EAAE,OAAa;QAChF,MAAM,aAAa,GAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAC9C,IAAI,OAAO,EAAE,CAAC;YACZ,aAAa,CAAC,OAAO,GAAG,OAAO,CAAC;QAClC,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,uBAAuB,CAAC,GAAa;QAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,GAAa,EAAE,UAAkB,aAAa,EAAE,OAAa;QACjF,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,GAAa,EAAE,UAAkB,cAAc;QACrE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,GAAa,EAAE,UAAkB,WAAW;QAC/D,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,GAAa,EAAE,UAAkB,WAAW;QAC9D,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,GAAa,EAAE,UAAkB,UAAU;QAC7D,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;CACF;AAhCD,oDAgCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/JWTService.d.ts b/SerpentRace_Backend/dist/Application/Services/JWTService.d.ts new file mode 100644 index 00000000..6915b51b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/JWTService.d.ts @@ -0,0 +1,27 @@ +import { Request, Response } from 'express'; +import { UserState } from '../../Domain/User/UserAggregate'; +export interface TokenPayload { + userId: string; + authLevel: 0 | 1; + userStatus: UserState; + orgId: string; + iat?: number; + exp?: number; +} +export declare class JWTService { + private readonly secretKey; + private readonly tokenExpiry; + private readonly cookieName; + constructor(); + create(payload: TokenPayload, res: Response): string; + verify(req: Request): TokenPayload | null; + shouldRefreshToken(payload: TokenPayload): boolean; + refreshIfNeeded(payload: TokenPayload, res: Response): boolean; + /** + * Parse duration string to seconds (e.g., "24h", "7d", "30m") + * @param duration Duration string + * @returns Duration in seconds + */ + private parseDuration; +} +//# sourceMappingURL=JWTService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/JWTService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/JWTService.d.ts.map new file mode 100644 index 00000000..ade9a70c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/JWTService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"JWTService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/JWTService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAE5D,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;;IAqBpC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM;IAuBpD,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI;IAazC,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAYlD,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO;IAe9D;;;;OAIG;IACH,OAAO,CAAC,aAAa;CAkBxB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/JWTService.js b/SerpentRace_Backend/dist/Application/Services/JWTService.js new file mode 100644 index 00000000..07e6f73a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/JWTService.js @@ -0,0 +1,102 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.JWTService = void 0; +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); +class JWTService { + constructor() { + this.secretKey = process.env.JWT_SECRET || 'your-secret-key'; + let expiry = 86400; + if (process.env.JWT_EXPIRY) { + expiry = parseInt(process.env.JWT_EXPIRY); + } + else if (process.env.JWT_EXPIRATION) { + expiry = this.parseDuration(process.env.JWT_EXPIRATION); + } + this.tokenExpiry = expiry; + this.cookieName = 'auth_token'; + if (process.env.NODE_ENV === 'production' && (!process.env.JWT_SECRET || process.env.JWT_SECRET === 'your-secret-key')) { + throw new Error('JWT_SECRET environment variable must be set in production'); + } + } + create(payload, res) { + const now = Math.floor(Date.now() / 1000); + const payloadWithTimestamps = { + ...payload, + iat: now, + exp: now + this.tokenExpiry + }; + // Don't use expiresIn option since we're manually setting exp in payload + const options = {}; + const token = jsonwebtoken_1.default.sign(payloadWithTimestamps, this.secretKey, options); + res.cookie(this.cookieName, token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'strict', + maxAge: this.tokenExpiry * 1000, // Convert to milliseconds + }); + return token; + } + verify(req) { + try { + const token = req.cookies[this.cookieName]; + if (!token) + return null; + const decoded = jsonwebtoken_1.default.verify(token, this.secretKey); + return decoded; + } + catch (error) { + return null; + } + } + // Check if token needs refresh (within 25% of expiry time) + shouldRefreshToken(payload) { + if (!payload.exp || !payload.iat) + return false; + const now = Math.floor(Date.now() / 1000); + const tokenAge = now - payload.iat; + const tokenLifetime = payload.exp - payload.iat; + const refreshThreshold = tokenLifetime * 0.75; // Refresh when 75% of lifetime has passed + return tokenAge >= refreshThreshold; + } + // Conditionally refresh token only if needed + refreshIfNeeded(payload, res) { + if (this.shouldRefreshToken(payload)) { + // Create new token with fresh timestamps, but same user data + const freshPayload = { + userId: payload.userId, + authLevel: payload.authLevel, + userStatus: payload.userStatus, + orgId: payload.orgId + }; + this.create(freshPayload, res); + return true; + } + return false; + } + /** + * Parse duration string to seconds (e.g., "24h", "7d", "30m") + * @param duration Duration string + * @returns Duration in seconds + */ + parseDuration(duration) { + const match = duration.match(/^(\d+)([smhd])$/); + if (!match) { + throw new Error(`Invalid duration format: ${duration}. Use format like '24h', '7d', '30m'`); + } + const [, value, unit] = match; + const num = parseInt(value); + switch (unit) { + case 's': return num; // seconds + case 'm': return num * 60; // minutes + case 'h': return num * 60 * 60; // hours + case 'd': return num * 60 * 60 * 24; // days + default: + throw new Error(`Unsupported duration unit: ${unit}`); + } + } +} +exports.JWTService = JWTService; +//# sourceMappingURL=JWTService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/JWTService.js.map b/SerpentRace_Backend/dist/Application/Services/JWTService.js.map new file mode 100644 index 00000000..b175daa8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/JWTService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"JWTService.js","sourceRoot":"","sources":["../../../src/Application/Services/JWTService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/Logger.d.ts b/SerpentRace_Backend/dist/Application/Services/Logger.d.ts new file mode 100644 index 00000000..222c7183 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/Logger.d.ts @@ -0,0 +1,14 @@ +import { LoggingService, LogLevel } from './LoggingService'; +import { Request, Response } from 'express'; +declare const logger: LoggingService; +export declare const logRequest: (message: string, req?: Request, res?: Response, metadata?: any) => void; +export declare const logError: (message: string, error?: Error, req?: Request, res?: Response) => void; +export declare const logWarning: (message: string, metadata?: any, req?: Request, res?: Response) => void; +export declare const logAuth: (message: string, userId?: string, metadata?: any, req?: Request, res?: Response) => void; +export declare const logDatabase: (message: string, query?: string, executionTime?: number, metadata?: any) => void; +export declare const logStartup: (message: string, metadata?: any) => void; +export declare const logConnection: (message: string, type: string, status: "success" | "failure" | "attempt", metadata?: any) => void; +export declare const logOther: (message: string, metadata?: any, req?: Request, res?: Response) => void; +export { LoggingService, LogLevel }; +export default logger; +//# sourceMappingURL=Logger.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/Logger.d.ts.map b/SerpentRace_Backend/dist/Application/Services/Logger.d.ts.map new file mode 100644 index 00000000..b395bbea --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/Logger.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/Logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C,QAAA,MAAM,MAAM,gBAA+B,CAAC;AAG5C,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,MAAM,OAAO,EAAE,MAAM,QAAQ,EAAE,WAAW,GAAG,SAExF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,EAAE,MAAM,OAAO,EAAE,MAAM,QAAQ,SAOrF,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,WAAW,GAAG,EAAE,MAAM,OAAO,EAAE,MAAM,QAAQ,SAExF,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,EAAE,SAAS,MAAM,EAAE,WAAW,GAAG,EAAE,MAAM,OAAO,EAAE,MAAM,QAAQ,SAMtG,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,EAAE,gBAAgB,MAAM,EAAE,WAAW,GAAG,SAOlG,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,WAAW,GAAG,SAEzD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,EAAE,MAAM,MAAM,EAAE,QAAQ,SAAS,GAAG,SAAS,GAAG,SAAS,EAAE,WAAW,GAAG,SAOrH,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,EAAE,WAAW,GAAG,EAAE,MAAM,OAAO,EAAE,MAAM,QAAQ,SAEtF,CAAC;AAGF,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;AACpC,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/Logger.js b/SerpentRace_Backend/dist/Application/Services/Logger.js new file mode 100644 index 00000000..243007e3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/Logger.js @@ -0,0 +1,62 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LogLevel = exports.LoggingService = exports.logOther = exports.logConnection = exports.logStartup = exports.logDatabase = exports.logAuth = exports.logWarning = exports.logError = exports.logRequest = void 0; +const LoggingService_1 = require("./LoggingService"); +Object.defineProperty(exports, "LoggingService", { enumerable: true, get: function () { return LoggingService_1.LoggingService; } }); +Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return LoggingService_1.LogLevel; } }); +// Singleton instance +const logger = LoggingService_1.LoggingService.getInstance(); +// Convenience functions for each log level +const logRequest = (message, req, res, metadata) => { + logger.log(LoggingService_1.LogLevel.REQUEST, message, metadata, req, res); +}; +exports.logRequest = logRequest; +const logError = (message, error, req, res) => { + const metadata = error ? { + name: error.name, + message: error.message, + stack: error.stack + } : undefined; + logger.log(LoggingService_1.LogLevel.ERROR, message, metadata, req, res); +}; +exports.logError = logError; +const logWarning = (message, metadata, req, res) => { + logger.log(LoggingService_1.LogLevel.WARNING, message, metadata, req, res); +}; +exports.logWarning = logWarning; +const logAuth = (message, userId, metadata, req, res) => { + const authMetadata = { + userId, + ...metadata + }; + logger.log(LoggingService_1.LogLevel.AUTH, message, authMetadata, req, res); +}; +exports.logAuth = logAuth; +const logDatabase = (message, query, executionTime, metadata) => { + const dbMetadata = { + query: query ? query.substring(0, 200) : undefined, + executionTime, + ...metadata + }; + logger.log(LoggingService_1.LogLevel.DATABASE, message, dbMetadata); +}; +exports.logDatabase = logDatabase; +const logStartup = (message, metadata) => { + logger.log(LoggingService_1.LogLevel.STARTUP, message, metadata); +}; +exports.logStartup = logStartup; +const logConnection = (message, type, status, metadata) => { + const connectionMetadata = { + connectionType: type, + status, + ...metadata + }; + logger.log(LoggingService_1.LogLevel.CONNECTION, message, connectionMetadata); +}; +exports.logConnection = logConnection; +const logOther = (message, metadata, req, res) => { + logger.log(LoggingService_1.LogLevel.OTHER, message, metadata, req, res); +}; +exports.logOther = logOther; +exports.default = logger; +//# sourceMappingURL=Logger.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/Logger.js.map b/SerpentRace_Backend/dist/Application/Services/Logger.js.map new file mode 100644 index 00000000..6055d5d9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/Logger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../../src/Application/Services/Logger.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/LoggingService.d.ts b/SerpentRace_Backend/dist/Application/Services/LoggingService.d.ts new file mode 100644 index 00000000..dc1c06a3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/LoggingService.d.ts @@ -0,0 +1,57 @@ +import { Request, Response, NextFunction } from 'express'; +export declare enum LogLevel { + REQUEST = "REQUEST", + ERROR = "ERROR", + WARNING = "WARNING", + AUTH = "AUTH", + DATABASE = "DATABASE", + STARTUP = "STARTUP", + CONNECTION = "CONNECTION", + OTHER = "OTHER" +} +export interface LogEntry { + timestamp: string; + level: LogLevel; + message: string; + metadata?: any; + requestId?: string; + userId?: string; + ip?: string; + userAgent?: string; + method?: string; + url?: string; + statusCode?: number; + responseTime?: number; +} +export declare class LoggingService { + private static instance; + private minioClient; + private logBuffer; + private currentLogFile; + private logCount; + private readonly maxLogsPerFile; + private readonly logsDir; + private readonly bucketName; + private uploadInterval; + private constructor(); + static getInstance(): LoggingService; + private initializeLogsDirectory; + private initializeMinioClient; + private ensureBucketExists; + private startPeriodicUpload; + private getMonthlyDirectory; + private getMonthlyMinioPrefix; + private createNewLogFile; + private formatLogEntry; + private writeToLocalFile; + private rotateLogFile; + private uploadToMinio; + private logToConsole; + log(level: LogLevel, message: string, metadata?: any, req?: Request, res?: Response, responseTime?: number): void; + private generateRequestId; + shutdown(): Promise; + requestLoggingMiddleware(): (req: Request, res: Response, next: NextFunction) => void; + errorLoggingMiddleware(): (error: Error, req: Request, res: Response, next: NextFunction) => void; +} +export default LoggingService; +//# sourceMappingURL=LoggingService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/LoggingService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/LoggingService.d.ts.map new file mode 100644 index 00000000..05374be8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/LoggingService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"LoggingService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/LoggingService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,UAAU,eAAe;IACzB,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IACxC,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsD;IACrF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuD;IAClF,OAAO,CAAC,cAAc,CAA+B;IAErD,OAAO;IAcP,MAAM,CAAC,WAAW,IAAI,cAAc;IAOpC,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,qBAAqB;YAqCf,kBAAkB;IAchC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,cAAc;YAmBR,gBAAgB;YAkBhB,aAAa;YAgBb,aAAa;IA2B3B,OAAO,CAAC,YAAY;IAwBb,GAAG,CACR,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,GAAG,EACd,GAAG,CAAC,EAAE,OAAO,EACb,GAAG,CAAC,EAAE,QAAQ,EACd,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI;IAuCP,OAAO,CAAC,iBAAiB;IAIZ,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB/B,wBAAwB,KACrB,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IA4BlD,sBAAsB,KACnB,OAAO,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;CAcxE;AAED,eAAe,cAAc,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/LoggingService.js b/SerpentRace_Backend/dist/Application/Services/LoggingService.js new file mode 100644 index 00000000..f707c6d0 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/LoggingService.js @@ -0,0 +1,363 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoggingService = exports.LogLevel = void 0; +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const Minio = __importStar(require("minio")); +var LogLevel; +(function (LogLevel) { + LogLevel["REQUEST"] = "REQUEST"; + LogLevel["ERROR"] = "ERROR"; + LogLevel["WARNING"] = "WARNING"; + LogLevel["AUTH"] = "AUTH"; + LogLevel["DATABASE"] = "DATABASE"; + LogLevel["STARTUP"] = "STARTUP"; + LogLevel["CONNECTION"] = "CONNECTION"; + LogLevel["OTHER"] = "OTHER"; +})(LogLevel || (exports.LogLevel = LogLevel = {})); +class LoggingService { + constructor() { + this.minioClient = null; + this.logBuffer = []; + this.currentLogFile = null; + this.logCount = 0; + this.maxLogsPerFile = parseInt(process.env.MAX_LOGS_PER_FILE || '10000'); + this.logsDir = path_1.default.join(process.cwd(), 'logs'); + this.bucketName = process.env.MINIO_BUCKET_NAME || 'serpentrace-logs'; + this.uploadInterval = null; + this.initializeLogsDirectory(); + this.initializeMinioClient(); + this.createNewLogFile(); + if (process.env.NODE_ENV !== 'test') { + this.startPeriodicUpload(); + } + process.on('SIGTERM', () => this.shutdown()); + process.on('SIGINT', () => this.shutdown()); + process.on('beforeExit', () => this.shutdown()); + } + static getInstance() { + if (!LoggingService.instance) { + LoggingService.instance = new LoggingService(); + } + return LoggingService.instance; + } + initializeLogsDirectory() { + try { + if (!fs_1.default.existsSync(this.logsDir)) { + fs_1.default.mkdirSync(this.logsDir, { recursive: true }); + } + // Create monthly subdirectory + const monthlyDir = this.getMonthlyDirectory(); + if (!fs_1.default.existsSync(monthlyDir)) { + fs_1.default.mkdirSync(monthlyDir, { recursive: true }); + } + } + catch (error) { + console.error('Failed to initialize logs directory:', error); + } + } + initializeMinioClient() { + try { + // Check if in production or development + if (process.env.NODE_ENV === 'production') { + if (process.env.MINIO_ENDPOINT && process.env.MINIO_ACCESS_KEY && process.env.MINIO_SECRET_KEY) { + this.minioClient = new Minio.Client({ + endPoint: process.env.MINIO_ENDPOINT, + port: parseInt(process.env.MINIO_PORT || '9000'), + useSSL: process.env.MINIO_USE_SSL === 'true', + accessKey: process.env.MINIO_ACCESS_KEY, + secretKey: process.env.MINIO_SECRET_KEY + }); + this.ensureBucketExists(); + } + else { + console.warn('Minio configuration not found. Logs will only be stored locally and in console.'); + } + } + else { + // Development-specific Minio configuration + this.minioClient = new Minio.Client({ + endPoint: 'localhost', + port: 9000, + useSSL: false, + accessKey: 'serpentrace', + secretKey: 'serpentrace123!' + }); + this.ensureBucketExists(); + } + } + catch (error) { + console.error('Failed to initialize Minio client:', error); + this.minioClient = null; + } + } + async ensureBucketExists() { + if (!this.minioClient) + return; + try { + const exists = await this.minioClient.bucketExists(this.bucketName); + if (!exists) { + await this.minioClient.makeBucket(this.bucketName); + this.log(LogLevel.STARTUP, `Created Minio bucket: ${this.bucketName}`); + } + } + catch (error) { + console.error('Failed to ensure bucket exists:', error); + } + } + startPeriodicUpload() { + // Upload current log file to Minio every 2 minutes + this.uploadInterval = setInterval(async () => { + if (this.currentLogFile && this.minioClient) { + await this.uploadToMinio(this.currentLogFile); + } + }, 2 * 60 * 1000); // 2 minutes + } + getMonthlyDirectory() { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + return path_1.default.join(this.logsDir, `${year}-${month}`); + } + getMonthlyMinioPrefix() { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + return `${year}-${month}/`; + } + createNewLogFile() { + const now = new Date(); + const timestamp = now.toISOString().replace(/[:.]/g, '-'); + const fileName = `serpentrace-${timestamp}.log`; + this.currentLogFile = path_1.default.join(this.getMonthlyDirectory(), fileName); + this.logCount = 0; + // Write log file header + const header = `# SerpentRace Backend Logs\n# Started: ${now.toISOString()}\n# Max entries per file: ${this.maxLogsPerFile}\n\n`; + try { + fs_1.default.writeFileSync(this.currentLogFile, header); + } + catch (error) { + console.error('Failed to create log file:', error); + } + } + formatLogEntry(entry) { + const parts = [ + entry.timestamp, + `[${entry.level}]`, + entry.message + ]; + if (entry.requestId) + parts.push(`ReqId:${entry.requestId}`); + if (entry.userId) + parts.push(`UserId:${entry.userId}`); + if (entry.ip) + parts.push(`IP:${entry.ip}`); + if (entry.method && entry.url) + parts.push(`${entry.method} ${entry.url}`); + if (entry.statusCode) + parts.push(`Status:${entry.statusCode}`); + if (entry.responseTime) + parts.push(`Time:${entry.responseTime}ms`); + if (entry.userAgent) + parts.push(`UA:${entry.userAgent.substring(0, 50)}`); + if (entry.metadata) + parts.push(`Meta:${JSON.stringify(entry.metadata)}`); + return parts.join(' | '); + } + async writeToLocalFile(entry) { + if (!this.currentLogFile) + return; + try { + const logLine = this.formatLogEntry(entry) + '\n'; + fs_1.default.appendFileSync(this.currentLogFile, logLine); + this.logCount++; + // Check if we need to rotate the log file + if (this.logCount >= this.maxLogsPerFile) { + await this.rotateLogFile(); + } + } + catch (error) { + console.error('Failed to write to log file:', error); + } + } + async rotateLogFile() { + if (!this.currentLogFile) + return; + try { + // Upload current file to Minio before rotating + await this.uploadToMinio(this.currentLogFile); + // Create new log file + this.createNewLogFile(); + this.log(LogLevel.OTHER, 'Log file rotated due to size limit'); + } + catch (error) { + console.error('Failed to rotate log file:', error); + } + } + async uploadToMinio(filePath) { + if (!this.minioClient) { + console.warn('Minio client not initialized, skipping upload'); + return; + } + if (!fs_1.default.existsSync(filePath)) { + console.warn(`Log file does not exist: ${filePath}`); + return; + } + try { + const fileName = path_1.default.basename(filePath); + const objectName = this.getMonthlyMinioPrefix() + fileName; + console.log(`Attempting to upload log file to Minio: ${objectName}`); + await this.minioClient.fPutObject(this.bucketName, objectName, filePath); + console.log(`Successfully uploaded log file to Minio: ${objectName}`); + } + catch (error) { + console.error('Failed to upload to Minio:', error); + console.error('Minio config:', { + endpoint: this.minioClient ? 'configured' : 'not configured', + bucket: this.bucketName + }); + } + } + logToConsole(entry) { + const formattedEntry = this.formatLogEntry(entry); + switch (entry.level) { + case LogLevel.ERROR: + console.error(formattedEntry); + break; + case LogLevel.WARNING: + console.warn(formattedEntry); + break; + case LogLevel.REQUEST: + case LogLevel.AUTH: + case LogLevel.DATABASE: + case LogLevel.CONNECTION: + console.info(formattedEntry); + break; + case LogLevel.STARTUP: + console.log(formattedEntry); + break; + default: + console.log(formattedEntry); + } + } + log(level, message, metadata, req, res, responseTime) { + const entry = { + timestamp: new Date().toISOString(), + level, + message, + metadata + }; + // Add request context if available + if (req) { + entry.requestId = req.requestId || this.generateRequestId(); + entry.userId = req.user?.userId; + entry.ip = req.ip || req.socket?.remoteAddress || 'unknown'; + entry.userAgent = req.get ? req.get('User-Agent') : 'unknown'; + entry.method = req.method; + entry.url = req.originalUrl || req.url; + } + if (res) { + entry.statusCode = res.statusCode; + } + if (responseTime !== undefined) { + entry.responseTime = responseTime; + } + // Log to all three destinations + this.logToConsole(entry); + this.writeToLocalFile(entry); + // Add to buffer for potential batch processing + this.logBuffer.push(entry); + // Limit buffer size + if (this.logBuffer.length > 1000) { + this.logBuffer = this.logBuffer.slice(-500); + } + } + generateRequestId() { + return Math.random().toString(36).substr(2, 9); + } + async shutdown() { + try { + // Clear the upload interval + if (this.uploadInterval) { + clearInterval(this.uploadInterval); + this.uploadInterval = null; + } + // Upload current log file to Minio + if (this.currentLogFile) { + await this.uploadToMinio(this.currentLogFile); + } + this.log(LogLevel.STARTUP, 'Logging service shutting down gracefully'); + // Give time for final logs to be written + await new Promise(resolve => setTimeout(resolve, 1000)); + } + catch (error) { + console.error('Error during logging service shutdown:', error); + } + } + // Middleware factory methods + requestLoggingMiddleware() { + return (req, res, next) => { + const startTime = Date.now(); + // Generate request ID + req.requestId = this.generateRequestId(); + // Log request start + this.log(LogLevel.REQUEST, `Incoming request`, undefined, req); + // Override res.end to log response + const originalEnd = res.end.bind(res); + res.end = (...args) => { + const responseTime = Date.now() - startTime; + LoggingService.getInstance().log(LogLevel.REQUEST, `Request completed`, undefined, req, res, responseTime); + return originalEnd(...args); + }; + next(); + }; + } + errorLoggingMiddleware() { + return (error, req, res, next) => { + this.log(LogLevel.ERROR, `Unhandled error: ${error.message}`, { + stack: error.stack, + name: error.name + }, req, res); + next(error); + }; + } +} +exports.LoggingService = LoggingService; +exports.default = LoggingService; +//# sourceMappingURL=LoggingService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/LoggingService.js.map b/SerpentRace_Backend/dist/Application/Services/LoggingService.js.map new file mode 100644 index 00000000..c539f993 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/LoggingService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LoggingService.js","sourceRoot":"","sources":["../../../src/Application/Services/LoggingService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/PasswordService.d.ts b/SerpentRace_Backend/dist/Application/Services/PasswordService.d.ts new file mode 100644 index 00000000..5ba1c1ce --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/PasswordService.d.ts @@ -0,0 +1,26 @@ +export declare class PasswordService { + private static readonly SALT_ROUNDS; + /** + * Hashes a plain text password using bcrypt + * @param password - The plain text password to hash + * @returns Promise - The hashed password + */ + static hashPassword(password: string): Promise; + /** + * Verifies a plain text password against a hashed password + * @param password - The plain text password to verify + * @param hashedPassword - The hashed password to compare against + * @returns Promise - True if password matches, false otherwise + */ + static verifyPassword(password: string, hashedPassword: string): Promise; + /** + * Validates password strength requirements + * @param password - The password to validate + * @returns object - Object containing isValid boolean and error messages + */ + static validatePasswordStrength(password: string): { + isValid: boolean; + errors: string[]; + }; +} +//# sourceMappingURL=PasswordService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/PasswordService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/PasswordService.d.ts.map new file mode 100644 index 00000000..393448f9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/PasswordService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"PasswordService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/PasswordService.ts"],"names":[],"mappings":"AAGA,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAM;IAEzC;;;;OAIG;WACU,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB5D;;;;;OAKG;WACU,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBvF;;;;OAIG;IACH,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;CAyC1F"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/PasswordService.js b/SerpentRace_Backend/dist/Application/Services/PasswordService.js new file mode 100644 index 00000000..09d1e849 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/PasswordService.js @@ -0,0 +1,124 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PasswordService = void 0; +const bcrypt = __importStar(require("bcrypt")); +const Logger_1 = require("./Logger"); +class PasswordService { + /** + * Hashes a plain text password using bcrypt + * @param password - The plain text password to hash + * @returns Promise - The hashed password + */ + static async hashPassword(password) { + try { + if (!password || typeof password !== 'string') { + throw new Error('Password must be a non-empty string'); + } + return await bcrypt.hash(password, this.SALT_ROUNDS); + } + catch (error) { + (0, Logger_1.logError)('PasswordService.hashPassword error', error instanceof Error ? error : new Error(String(error))); + if (error instanceof Error && error.message === 'Password must be a non-empty string') { + throw error; // Re-throw validation errors as-is + } + throw new Error('Failed to hash password'); + } + } + /** + * Verifies a plain text password against a hashed password + * @param password - The plain text password to verify + * @param hashedPassword - The hashed password to compare against + * @returns Promise - True if password matches, false otherwise + */ + static async verifyPassword(password, hashedPassword) { + try { + if (!password || typeof password !== 'string') { + return false; // Invalid input should return false, not throw + } + if (!hashedPassword || typeof hashedPassword !== 'string') { + return false; // Invalid input should return false, not throw + } + return await bcrypt.compare(password, hashedPassword); + } + catch (error) { + (0, Logger_1.logError)('PasswordService.verifyPassword error', error instanceof Error ? error : new Error(String(error))); + return false; // Return false on error instead of throwing + } + } + /** + * Validates password strength requirements + * @param password - The password to validate + * @returns object - Object containing isValid boolean and error messages + */ + static validatePasswordStrength(password) { + try { + const errors = []; + if (!password || typeof password !== 'string') { + errors.push('Password must be provided as a string'); + return { isValid: false, errors }; + } + if (password.length < 8) { + errors.push('Password must be at least 8 characters long'); + } + if (!/[A-Z]/.test(password)) { + errors.push('Password must contain at least one uppercase letter'); + } + if (!/[a-z]/.test(password)) { + errors.push('Password must contain at least one lowercase letter'); + } + if (!/\d/.test(password)) { + errors.push('Password must contain at least one number'); + } + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { + errors.push('Password must contain at least one special character'); + } + return { + isValid: errors.length === 0, + errors + }; + } + catch (error) { + (0, Logger_1.logError)('PasswordService.validatePasswordStrength error', error instanceof Error ? error : new Error(String(error))); + return { + isValid: false, + errors: ['Password validation failed due to internal error'] + }; + } + } +} +exports.PasswordService = PasswordService; +PasswordService.SALT_ROUNDS = 12; +//# sourceMappingURL=PasswordService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/PasswordService.js.map b/SerpentRace_Backend/dist/Application/Services/PasswordService.js.map new file mode 100644 index 00000000..cd4e8da4 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/PasswordService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PasswordService.js","sourceRoot":"","sources":["../../../src/Application/Services/PasswordService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/RedisService.d.ts b/SerpentRace_Backend/dist/Application/Services/RedisService.d.ts new file mode 100644 index 00000000..27acf798 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/RedisService.d.ts @@ -0,0 +1,40 @@ +export interface ActiveChatData { + chatId: string; + participants: string[]; + lastActivity: Date; + messageCount: number; + chatType: 'direct' | 'group' | 'game'; + gameId?: string; + name?: string; +} +export interface ActiveUserData { + userId: string; + activeChatIds: string[]; + lastActivity: Date; + isOnline: boolean; +} +export declare class RedisService { + private static instance; + private client; + private isConnected; + private constructor(); + static getInstance(): RedisService; + connect(): Promise; + disconnect(): Promise; + setActiveChat(chatId: string, chatData: ActiveChatData): Promise; + getActiveChat(chatId: string): Promise; + removeActiveChat(chatId: string): Promise; + getAllActiveChats(): Promise; + setActiveUser(userId: string, userData: ActiveUserData): Promise; + getActiveUser(userId: string): Promise; + removeActiveUser(userId: string): Promise; + addUserToChat(userId: string, chatId: string): Promise; + removeUserFromChat(userId: string, chatId: string): Promise; + getUserActiveChats(userId: string): Promise; + updateChatActivity(chatId: string, messageCount?: number): Promise; + getInactiveChats(inactivityMinutes: number): Promise; + cleanupInactiveChats(inactivityMinutes: number): Promise; + ping(): Promise; + isRedisConnected(): boolean; +} +//# sourceMappingURL=RedisService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/RedisService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/RedisService.d.ts.map new file mode 100644 index 00000000..40fe8718 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/RedisService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RedisService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/RedisService.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,IAAI,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,IAAI,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAe;IACtC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,WAAW,CAAkB;IAErC,OAAO;WAyBO,WAAW,IAAI,YAAY;IAO5B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAWxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAwB7D,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,iBAAiB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IA4B9C,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqB7D,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5D,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajE,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAUrD,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAexE,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAc9D,oBAAoB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAelE,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAU9B,gBAAgB,IAAI,OAAO;CAGrC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/RedisService.js b/SerpentRace_Backend/dist/Application/Services/RedisService.js new file mode 100644 index 00000000..50747941 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/RedisService.js @@ -0,0 +1,273 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisService = void 0; +const redis_1 = require("redis"); +const Logger_1 = require("./Logger"); +class RedisService { + constructor() { + this.isConnected = false; + const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; + this.client = (0, redis_1.createClient)({ + url: redisUrl, + socket: { + reconnectStrategy: (retries) => Math.min(retries * 50, 500) + } + }); + this.client.on('error', (err) => { + (0, Logger_1.logError)('Redis connection error', err); + this.isConnected = false; + }); + this.client.on('connect', () => { + (0, Logger_1.logStartup)('Redis client connected successfully'); + this.isConnected = true; + }); + this.client.on('disconnect', () => { + (0, Logger_1.logWarning)('Redis client disconnected'); + this.isConnected = false; + }); + } + static getInstance() { + if (!RedisService.instance) { + RedisService.instance = new RedisService(); + } + return RedisService.instance; + } + async connect() { + try { + if (!this.isConnected) { + await this.client.connect(); + } + } + catch (error) { + (0, Logger_1.logError)('Failed to connect to Redis', error); + throw error; + } + } + async disconnect() { + try { + if (this.isConnected) { + await this.client.disconnect(); + } + } + catch (error) { + (0, Logger_1.logError)('Failed to disconnect from Redis', error); + } + } + async setActiveChat(chatId, chatData) { + try { + const key = `active_chat:${chatId}`; + await this.client.hSet(key, { + chatId: chatData.chatId, + participants: JSON.stringify(chatData.participants), + lastActivity: chatData.lastActivity.toISOString(), + messageCount: chatData.messageCount.toString(), + chatType: chatData.chatType, + gameId: chatData.gameId || '', + name: chatData.name || '' + }); + // Set expiration for 1 hour of inactivity + await this.client.expire(key, 3600); + } + catch (error) { + (0, Logger_1.logError)(`Failed to set active chat ${chatId}`, error); + } + } + async getActiveChat(chatId) { + try { + const key = `active_chat:${chatId}`; + const data = await this.client.hGetAll(key); + if (!data.chatId) { + return null; + } + return { + chatId: data.chatId, + participants: JSON.parse(data.participants), + lastActivity: new Date(data.lastActivity), + messageCount: parseInt(data.messageCount, 10), + chatType: data.chatType, + gameId: data.gameId || undefined, + name: data.name || undefined + }; + } + catch (error) { + (0, Logger_1.logError)(`Failed to get active chat ${chatId}`, error); + return null; + } + } + async removeActiveChat(chatId) { + try { + const key = `active_chat:${chatId}`; + await this.client.del(key); + } + catch (error) { + (0, Logger_1.logError)(`Failed to remove active chat ${chatId}`, error); + } + } + async getAllActiveChats() { + try { + const pattern = 'active_chat:*'; + const keys = await this.client.keys(pattern); + const chats = []; + for (const key of keys) { + const data = await this.client.hGetAll(key); + if (data.chatId) { + chats.push({ + chatId: data.chatId, + participants: JSON.parse(data.participants), + lastActivity: new Date(data.lastActivity), + messageCount: parseInt(data.messageCount, 10), + chatType: data.chatType, + gameId: data.gameId || undefined, + name: data.name || undefined + }); + } + } + return chats; + } + catch (error) { + (0, Logger_1.logError)('Failed to get all active chats', error); + return []; + } + } + async setActiveUser(userId, userData) { + try { + const key = `active_user:${userId}`; + await this.client.hSet(key, { + userId: userData.userId, + activeChatIds: JSON.stringify(userData.activeChatIds), + lastActivity: userData.lastActivity.toISOString(), + isOnline: userData.isOnline.toString() + }); + // Set expiration for 2 hours + await this.client.expire(key, 7200); + } + catch (error) { + (0, Logger_1.logError)(`Failed to set active user ${userId}`, error); + } + } + async getActiveUser(userId) { + try { + const key = `active_user:${userId}`; + const data = await this.client.hGetAll(key); + if (!data.userId) { + return null; + } + return { + userId: data.userId, + activeChatIds: JSON.parse(data.activeChatIds), + lastActivity: new Date(data.lastActivity), + isOnline: data.isOnline === 'true' + }; + } + catch (error) { + (0, Logger_1.logError)(`Failed to get active user ${userId}`, error); + return null; + } + } + async removeActiveUser(userId) { + try { + const key = `active_user:${userId}`; + await this.client.del(key); + } + catch (error) { + (0, Logger_1.logError)(`Failed to remove active user ${userId}`, error); + } + } + async addUserToChat(userId, chatId) { + try { + const userData = await this.getActiveUser(userId) || { + userId, + activeChatIds: [], + lastActivity: new Date(), + isOnline: true + }; + if (!userData.activeChatIds.includes(chatId)) { + userData.activeChatIds.push(chatId); + userData.lastActivity = new Date(); + await this.setActiveUser(userId, userData); + } + } + catch (error) { + (0, Logger_1.logError)(`Failed to add user ${userId} to chat ${chatId}`, error); + } + } + async removeUserFromChat(userId, chatId) { + try { + const userData = await this.getActiveUser(userId); + if (userData) { + userData.activeChatIds = userData.activeChatIds.filter(id => id !== chatId); + userData.lastActivity = new Date(); + await this.setActiveUser(userId, userData); + } + } + catch (error) { + (0, Logger_1.logError)(`Failed to remove user ${userId} from chat ${chatId}`, error); + } + } + async getUserActiveChats(userId) { + try { + const userData = await this.getActiveUser(userId); + return userData?.activeChatIds || []; + } + catch (error) { + (0, Logger_1.logError)(`Failed to get active chats for user ${userId}`, error); + return []; + } + } + async updateChatActivity(chatId, messageCount) { + try { + const chatData = await this.getActiveChat(chatId); + if (chatData) { + chatData.lastActivity = new Date(); + if (messageCount !== undefined) { + chatData.messageCount = messageCount; + } + await this.setActiveChat(chatId, chatData); + } + } + catch (error) { + (0, Logger_1.logError)(`Failed to update chat activity ${chatId}`, error); + } + } + async getInactiveChats(inactivityMinutes) { + try { + const cutoffTime = new Date(Date.now() - inactivityMinutes * 60 * 1000); + const allChats = await this.getAllActiveChats(); + return allChats + .filter(chat => chat.lastActivity < cutoffTime) + .map(chat => chat.chatId); + } + catch (error) { + (0, Logger_1.logError)('Failed to get inactive chats', error); + return []; + } + } + async cleanupInactiveChats(inactivityMinutes) { + try { + const inactiveChats = await this.getInactiveChats(inactivityMinutes); + for (const chatId of inactiveChats) { + await this.removeActiveChat(chatId); + } + return inactiveChats; + } + catch (error) { + (0, Logger_1.logError)('Failed to cleanup inactive chats', error); + return []; + } + } + async ping() { + try { + const result = await this.client.ping(); + return result === 'PONG'; + } + catch (error) { + (0, Logger_1.logError)('Redis ping failed', error); + return false; + } + } + isRedisConnected() { + return this.isConnected; + } +} +exports.RedisService = RedisService; +//# sourceMappingURL=RedisService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/RedisService.js.map b/SerpentRace_Backend/dist/Application/Services/RedisService.js.map new file mode 100644 index 00000000..8319ace6 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/RedisService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RedisService.js","sourceRoot":"","sources":["../../../src/Application/Services/RedisService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/TokenService.d.ts b/SerpentRace_Backend/dist/Application/Services/TokenService.d.ts new file mode 100644 index 00000000..713225d2 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/TokenService.d.ts @@ -0,0 +1,80 @@ +export interface VerificationToken { + token: string; + expiresAt: Date; + createdAt: Date; +} +export interface PasswordResetToken { + token: string; + expiresAt: Date; + createdAt: Date; +} +export declare class TokenService { + private static readonly VERIFICATION_TOKEN_EXPIRES_HOURS; + private static readonly PASSWORD_RESET_TOKEN_EXPIRES_HOURS; + private static readonly TOKEN_LENGTH; + /** + * Generate a secure random token + * @param length - Length of the token in bytes (default: 32) + * @returns Hexadecimal string token + */ + static generateSecureToken(length?: number): string; + /** + * Generate email verification token with expiration + * @returns VerificationToken object with token and expiration info + */ + static generateVerificationToken(): VerificationToken; + /** + * Generate password reset token with expiration + * @returns PasswordResetToken object with token and expiration info + */ + static generatePasswordResetToken(): PasswordResetToken; + /** + * Check if a token has expired + * @param expiresAt - Expiration date of the token + * @returns True if token has expired, false otherwise + */ + static isTokenExpired(expiresAt: Date): boolean; + /** + * Validate token format (basic validation) + * @param token - Token to validate + * @returns True if token format is valid, false otherwise + */ + static isValidTokenFormat(token: string): boolean; + /** + * Generate a verification URL with token + * @param baseUrl - Base URL of the application + * @param token - Verification token + * @returns Complete verification URL + */ + static generateVerificationUrl(baseUrl: string, token: string): string; + /** + * Generate a password reset URL with token + * @param baseUrl - Base URL of the application + * @param token - Password reset token + * @returns Complete password reset URL + */ + static generatePasswordResetUrl(baseUrl: string, token: string): string; + /** + * Hash a token for secure storage in database + * @param token - Plain text token to hash + * @returns Hashed token + */ + static hashToken(token: string): Promise; + /** + * Verify a plain text token against a hashed token + * @param plainToken - Plain text token to verify + * @param hashedToken - Hashed token to compare against + * @returns True if tokens match, false otherwise + */ + static verifyToken(plainToken: string, hashedToken: string): Promise; + /** + * Get token expiration info in human-readable format + * @param expiresAt - Expiration date + * @returns Human-readable expiration info + */ + static getExpirationInfo(expiresAt: Date): { + expired: boolean; + timeLeft: string; + }; +} +//# sourceMappingURL=TokenService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/TokenService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/TokenService.d.ts.map new file mode 100644 index 00000000..b3db302e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/TokenService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"TokenService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/TokenService.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gCAAgC,CAAM;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kCAAkC,CAAK;IAC/D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAM;IAE1C;;;;OAIG;IACH,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAE,MAAkC,GAAG,MAAM;IAS9E;;;OAGG;IACH,MAAM,CAAC,yBAAyB,IAAI,iBAAiB;IAiBrD;;;OAGG;IACH,MAAM,CAAC,0BAA0B,IAAI,kBAAkB;IAiBvD;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO;IAS/C;;;;OAIG;IACH,MAAM,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAiBjD;;;;;OAKG;IACH,MAAM,CAAC,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAWtE;;;;;OAKG;IACH,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAWvE;;;;OAIG;WACU,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAatD;;;;;OAKG;WACU,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcnF;;;;OAIG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,IAAI,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;CAuClF"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/TokenService.js b/SerpentRace_Backend/dist/Application/Services/TokenService.js new file mode 100644 index 00000000..b26a3ea5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/TokenService.js @@ -0,0 +1,245 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TokenService = void 0; +const crypto = __importStar(require("crypto")); +const Logger_1 = require("./Logger"); +class TokenService { + /** + * Generate a secure random token + * @param length - Length of the token in bytes (default: 32) + * @returns Hexadecimal string token + */ + static generateSecureToken(length = TokenService.TOKEN_LENGTH) { + try { + return crypto.randomBytes(length).toString('hex'); + } + catch (error) { + (0, Logger_1.logError)('TokenService.generateSecureToken error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to generate secure token'); + } + } + /** + * Generate email verification token with expiration + * @returns VerificationToken object with token and expiration info + */ + static generateVerificationToken() { + try { + const token = this.generateSecureToken(); + const createdAt = new Date(); + const expiresAt = new Date(createdAt.getTime() + (this.VERIFICATION_TOKEN_EXPIRES_HOURS * 60 * 60 * 1000)); + return { + token, + createdAt, + expiresAt + }; + } + catch (error) { + (0, Logger_1.logError)('TokenService.generateVerificationToken error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to generate verification token'); + } + } + /** + * Generate password reset token with expiration + * @returns PasswordResetToken object with token and expiration info + */ + static generatePasswordResetToken() { + try { + const token = this.generateSecureToken(); + const createdAt = new Date(); + const expiresAt = new Date(createdAt.getTime() + (this.PASSWORD_RESET_TOKEN_EXPIRES_HOURS * 60 * 60 * 1000)); + return { + token, + createdAt, + expiresAt + }; + } + catch (error) { + (0, Logger_1.logError)('TokenService.generatePasswordResetToken error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to generate password reset token'); + } + } + /** + * Check if a token has expired + * @param expiresAt - Expiration date of the token + * @returns True if token has expired, false otherwise + */ + static isTokenExpired(expiresAt) { + try { + return new Date() > expiresAt; + } + catch (error) { + (0, Logger_1.logError)('TokenService.isTokenExpired error', error instanceof Error ? error : new Error(String(error))); + return true; // Assume expired on error for security + } + } + /** + * Validate token format (basic validation) + * @param token - Token to validate + * @returns True if token format is valid, false otherwise + */ + static isValidTokenFormat(token) { + try { + if (!token || typeof token !== 'string') { + return false; + } + // Check if token is hexadecimal and has expected length + const hexRegex = /^[a-f0-9]+$/i; + const expectedLength = this.TOKEN_LENGTH * 2; // Each byte becomes 2 hex characters + return hexRegex.test(token) && token.length === expectedLength; + } + catch (error) { + (0, Logger_1.logError)('TokenService.isValidTokenFormat error', error instanceof Error ? error : new Error(String(error))); + return false; + } + } + /** + * Generate a verification URL with token + * @param baseUrl - Base URL of the application + * @param token - Verification token + * @returns Complete verification URL + */ + static generateVerificationUrl(baseUrl, token) { + try { + // Remove trailing slash from baseUrl if present + const cleanBaseUrl = baseUrl.replace(/\/$/, ''); + return `${cleanBaseUrl}/api/auth/verify-email?token=${encodeURIComponent(token)}`; + } + catch (error) { + (0, Logger_1.logError)('TokenService.generateVerificationUrl error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to generate verification URL'); + } + } + /** + * Generate a password reset URL with token + * @param baseUrl - Base URL of the application + * @param token - Password reset token + * @returns Complete password reset URL + */ + static generatePasswordResetUrl(baseUrl, token) { + try { + // Remove trailing slash from baseUrl if present + const cleanBaseUrl = baseUrl.replace(/\/$/, ''); + return `${cleanBaseUrl}/api/auth/reset-password?token=${encodeURIComponent(token)}`; + } + catch (error) { + (0, Logger_1.logError)('TokenService.generatePasswordResetUrl error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to generate password reset URL'); + } + } + /** + * Hash a token for secure storage in database + * @param token - Plain text token to hash + * @returns Hashed token + */ + static async hashToken(token) { + try { + if (!token || typeof token !== 'string') { + throw new Error('Token must be a non-empty string'); + } + return crypto.createHash('sha256').update(token).digest('hex'); + } + catch (error) { + (0, Logger_1.logError)('TokenService.hashToken error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to hash token'); + } + } + /** + * Verify a plain text token against a hashed token + * @param plainToken - Plain text token to verify + * @param hashedToken - Hashed token to compare against + * @returns True if tokens match, false otherwise + */ + static async verifyToken(plainToken, hashedToken) { + try { + if (!plainToken || !hashedToken) { + return false; + } + const hashedPlainToken = await this.hashToken(plainToken); + return hashedPlainToken === hashedToken; + } + catch (error) { + (0, Logger_1.logError)('TokenService.verifyToken error', error instanceof Error ? error : new Error(String(error))); + return false; + } + } + /** + * Get token expiration info in human-readable format + * @param expiresAt - Expiration date + * @returns Human-readable expiration info + */ + static getExpirationInfo(expiresAt) { + try { + const now = new Date(); + const expired = now > expiresAt; + if (expired) { + const timeAgo = Math.floor((now.getTime() - expiresAt.getTime()) / (1000 * 60)); + return { + expired: true, + timeLeft: `Expired ${timeAgo} minute(s) ago` + }; + } + const timeLeft = Math.floor((expiresAt.getTime() - now.getTime()) / (1000 * 60)); + const hours = Math.floor(timeLeft / 60); + const minutes = timeLeft % 60; + let timeString = ''; + if (hours > 0) { + timeString = `${hours} hour(s)`; + if (minutes > 0) { + timeString += ` and ${minutes} minute(s)`; + } + } + else { + timeString = `${minutes} minute(s)`; + } + return { + expired: false, + timeLeft: `Expires in ${timeString}` + }; + } + catch (error) { + (0, Logger_1.logError)('TokenService.getExpirationInfo error', error instanceof Error ? error : new Error(String(error))); + return { + expired: true, + timeLeft: 'Unable to determine expiration' + }; + } + } +} +exports.TokenService = TokenService; +TokenService.VERIFICATION_TOKEN_EXPIRES_HOURS = 24; +TokenService.PASSWORD_RESET_TOKEN_EXPIRES_HOURS = 1; +TokenService.TOKEN_LENGTH = 32; +//# sourceMappingURL=TokenService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/TokenService.js.map b/SerpentRace_Backend/dist/Application/Services/TokenService.js.map new file mode 100644 index 00000000..eb73ba13 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/TokenService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TokenService.js","sourceRoot":"","sources":["../../../src/Application/Services/TokenService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,qCAAoC;AAcpC,MAAa,YAAY;IAKvB;;;;OAIG;IACH,MAAM,CAAC,mBAAmB,CAAC,SAAiB,YAAY,CAAC,YAAY;QACnE,IAAI,CAAC;YACH,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,wCAAwC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9G,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,yBAAyB;QAC9B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACzC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,gCAAgC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YAE3G,OAAO;gBACL,KAAK;gBACL,SAAS;gBACT,SAAS;aACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,8CAA8C,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACpH,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,0BAA0B;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACzC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,kCAAkC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YAE7G,OAAO;gBACL,KAAK;gBACL,SAAS;gBACT,SAAS;aACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,+CAA+C,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrH,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,SAAe;QACnC,IAAI,CAAC;YACH,OAAO,IAAI,IAAI,EAAE,GAAG,SAAS,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,mCAAmC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACzG,OAAO,IAAI,CAAC,CAAC,uCAAuC;QACtD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,kBAAkB,CAAC,KAAa;QACrC,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACxC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,wDAAwD;YACxD,MAAM,QAAQ,GAAG,cAAc,CAAC;YAChC,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,qCAAqC;YAEnF,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,uCAAuC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7G,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,uBAAuB,CAAC,OAAe,EAAE,KAAa;QAC3D,IAAI,CAAC;YACH,gDAAgD;YAChD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAChD,OAAO,GAAG,YAAY,gCAAgC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;QACpF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,4CAA4C,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAClH,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,wBAAwB,CAAC,OAAe,EAAE,KAAa;QAC5D,IAAI,CAAC;YACH,gDAAgD;YAChD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAChD,OAAO,GAAG,YAAY,kCAAkC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;QACtF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,6CAA6C,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACnH,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAa;QAClC,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACxC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACtD,CAAC;YAED,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,8BAA8B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACpG,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,UAAkB,EAAE,WAAmB;QAC9D,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;gBAChC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAC1D,OAAO,gBAAgB,KAAK,WAAW,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,gCAAgC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACtG,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAe;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,GAAG,GAAG,SAAS,CAAC;YAEhC,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;gBAChF,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,QAAQ,EAAE,WAAW,OAAO,gBAAgB;iBAC7C,CAAC;YACJ,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;YACjF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,QAAQ,GAAG,EAAE,CAAC;YAE9B,IAAI,UAAU,GAAG,EAAE,CAAC;YACpB,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,UAAU,GAAG,GAAG,KAAK,UAAU,CAAC;gBAChC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,UAAU,IAAI,QAAQ,OAAO,YAAY,CAAC;gBAC5C,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,UAAU,GAAG,GAAG,OAAO,YAAY,CAAC;YACtC,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,cAAc,UAAU,EAAE;aACrC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,sCAAsC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5G,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,gCAAgC;aAC3C,CAAC;QACJ,CAAC;IACH,CAAC;;AApNH,oCAqNC;AApNyB,6CAAgC,GAAG,EAAE,CAAC;AACtC,+CAAkC,GAAG,CAAC,CAAC;AACvC,yBAAY,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.d.ts b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.d.ts new file mode 100644 index 00000000..0d89fc80 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.d.ts @@ -0,0 +1,64 @@ +import { Request, Response, NextFunction } from 'express'; +/** + * Common validation middleware functions for request validation + */ +export declare class ValidationMiddleware { + /** + * Validates required fields in request body + * @param requiredFields Array of required field names + */ + static validateRequiredFields(requiredFields: string[]): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates field types in request body + * @param fieldTypes Object mapping field names to expected types + */ + static validateFieldTypes(fieldTypes: Record): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates string field length constraints + * @param constraints Object mapping field names to min/max length + */ + static validateStringLength(constraints: Record): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates email format + * @param emailFields Array of field names that should contain valid emails + */ + static validateEmailFormat(emailFields: string[]): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates UUIDs format + * @param uuidFields Array of field names that should contain valid UUIDs + */ + static validateUUIDFormat(uuidFields: string[]): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates numeric constraints + * @param constraints Object mapping field names to min/max values + */ + static validateNumericConstraints(constraints: Record): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates that arrays are not empty + * @param arrayFields Array of field names that should contain non-empty arrays + */ + static validateNonEmptyArrays(arrayFields: string[]): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Validates allowed values for enum-like fields + * @param allowedValues Object mapping field names to arrays of allowed values + */ + static validateAllowedValues(allowedValues: Record): (req: Request, res: Response, next: NextFunction) => Response> | undefined; + /** + * Combines multiple validation middlewares + * @param validations Array of validation middleware functions + */ + static combine(validations: Array<(req: Request, res: Response, next: NextFunction) => void>): (req: Request, res: Response, next: NextFunction) => Promise; + /** + * Helper method to get nested values from request + * @param req Request object + * @param path Dot-notation path like 'body.user.id' + */ + private static getNestedValue; +} +//# sourceMappingURL=ValidationMiddleware.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.d.ts.map b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.d.ts.map new file mode 100644 index 00000000..9b0c9f28 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ValidationMiddleware.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/ValidationMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI1D;;GAEG;AACH,qBAAa,oBAAoB;IAE7B;;;OAGG;IACH,MAAM,CAAC,sBAAsB,CAAC,cAAc,EAAE,MAAM,EAAE,IAC1C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IAyB3D;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC,IAC9F,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IA6B3D;;;OAGG;IACH,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,IAC3E,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IAiC3D;;;OAGG;IACH,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,IACpC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IA4B3D;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,IAClC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IAgC3D;;;OAGG;IACH,MAAM,CAAC,0BAA0B,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,IACjF,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IAiC3D;;;OAGG;IACH,MAAM,CAAC,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,IACvC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IA6B3D;;;OAGG;IACH,MAAM,CAAC,qBAAqB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IACrD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IA2B3D;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC,IAC1E,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY;IA+BjE;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;CAchC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js new file mode 100644 index 00000000..5105f541 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js @@ -0,0 +1,268 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ValidationMiddleware = void 0; +const ErrorResponseService_1 = require("./ErrorResponseService"); +const Logger_1 = require("./Logger"); +/** + * Common validation middleware functions for request validation + */ +class ValidationMiddleware { + /** + * Validates required fields in request body + * @param requiredFields Array of required field names + */ + static validateRequiredFields(requiredFields) { + return (req, res, next) => { + const missingFields = []; + for (const field of requiredFields) { + if (!req.body || req.body[field] === undefined || req.body[field] === null || req.body[field] === '') { + missingFields.push(field); + } + } + if (missingFields.length > 0) { + (0, Logger_1.logWarning)('Validation failed - missing required fields', { + missingFields, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Missing required fields', { missingFields }); + } + next(); + }; + } + /** + * Validates field types in request body + * @param fieldTypes Object mapping field names to expected types + */ + static validateFieldTypes(fieldTypes) { + return (req, res, next) => { + const typeErrors = []; + for (const [field, expectedType] of Object.entries(fieldTypes)) { + if (req.body && req.body[field] !== undefined) { + const actualType = Array.isArray(req.body[field]) ? 'array' : typeof req.body[field]; + if (actualType !== expectedType) { + typeErrors.push(`Field '${field}' should be ${expectedType}, got ${actualType}`); + } + } + } + if (typeErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - invalid field types', { + typeErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Invalid field types', { errors: typeErrors }); + } + next(); + }; + } + /** + * Validates string field length constraints + * @param constraints Object mapping field names to min/max length + */ + static validateStringLength(constraints) { + return (req, res, next) => { + const lengthErrors = []; + for (const [field, constraint] of Object.entries(constraints)) { + if (req.body && typeof req.body[field] === 'string') { + const value = req.body[field]; + if (constraint.min !== undefined && value.length < constraint.min) { + lengthErrors.push(`Field '${field}' must be at least ${constraint.min} characters`); + } + if (constraint.max !== undefined && value.length > constraint.max) { + lengthErrors.push(`Field '${field}' must not exceed ${constraint.max} characters`); + } + } + } + if (lengthErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - string length constraints', { + lengthErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'String length validation failed', { errors: lengthErrors }); + } + next(); + }; + } + /** + * Validates email format + * @param emailFields Array of field names that should contain valid emails + */ + static validateEmailFormat(emailFields) { + return (req, res, next) => { + const emailErrors = []; + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + for (const field of emailFields) { + if (req.body && req.body[field] && typeof req.body[field] === 'string') { + if (!emailRegex.test(req.body[field])) { + emailErrors.push(`Field '${field}' must contain a valid email address`); + } + } + } + if (emailErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - invalid email format', { + emailErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Email format validation failed', { errors: emailErrors }); + } + next(); + }; + } + /** + * Validates UUIDs format + * @param uuidFields Array of field names that should contain valid UUIDs + */ + static validateUUIDFormat(uuidFields) { + return (req, res, next) => { + const uuidErrors = []; + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + for (const field of uuidFields) { + const value = field.includes('.') + ? this.getNestedValue(req, field) + : req.body?.[field] || req.params?.[field] || req.query?.[field]; + if (value && typeof value === 'string') { + if (!uuidRegex.test(value)) { + uuidErrors.push(`Field '${field}' must contain a valid UUID`); + } + } + } + if (uuidErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - invalid UUID format', { + uuidErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'UUID format validation failed', { errors: uuidErrors }); + } + next(); + }; + } + /** + * Validates numeric constraints + * @param constraints Object mapping field names to min/max values + */ + static validateNumericConstraints(constraints) { + return (req, res, next) => { + const numericErrors = []; + for (const [field, constraint] of Object.entries(constraints)) { + if (req.body && typeof req.body[field] === 'number') { + const value = req.body[field]; + if (constraint.min !== undefined && value < constraint.min) { + numericErrors.push(`Field '${field}' must be at least ${constraint.min}`); + } + if (constraint.max !== undefined && value > constraint.max) { + numericErrors.push(`Field '${field}' must not exceed ${constraint.max}`); + } + } + } + if (numericErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - numeric constraints', { + numericErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Numeric validation failed', { errors: numericErrors }); + } + next(); + }; + } + /** + * Validates that arrays are not empty + * @param arrayFields Array of field names that should contain non-empty arrays + */ + static validateNonEmptyArrays(arrayFields) { + return (req, res, next) => { + const arrayErrors = []; + for (const field of arrayFields) { + if (req.body && Array.isArray(req.body[field])) { + if (req.body[field].length === 0) { + arrayErrors.push(`Field '${field}' must not be empty`); + } + } + else if (req.body && req.body[field] !== undefined) { + arrayErrors.push(`Field '${field}' must be an array`); + } + } + if (arrayErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - empty arrays', { + arrayErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Array validation failed', { errors: arrayErrors }); + } + next(); + }; + } + /** + * Validates allowed values for enum-like fields + * @param allowedValues Object mapping field names to arrays of allowed values + */ + static validateAllowedValues(allowedValues) { + return (req, res, next) => { + const valueErrors = []; + for (const [field, allowed] of Object.entries(allowedValues)) { + if (req.body && req.body[field] !== undefined) { + if (!allowed.includes(req.body[field])) { + valueErrors.push(`Field '${field}' must be one of: ${allowed.join(', ')}`); + } + } + } + if (valueErrors.length > 0) { + (0, Logger_1.logWarning)('Validation failed - disallowed values', { + valueErrors, + endpoint: req.path + }, req, res); + return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Value validation failed', { errors: valueErrors }); + } + next(); + }; + } + /** + * Combines multiple validation middlewares + * @param validations Array of validation middleware functions + */ + static combine(validations) { + return async (req, res, next) => { + let currentIndex = 0; + const runNext = (error) => { + if (error) { + return next(error); + } + if (currentIndex >= validations.length) { + return next(); + } + const currentValidation = validations[currentIndex++]; + try { + currentValidation(req, res, (err) => { + if (res.headersSent) { + return; // Response already sent, don't continue + } + runNext(err); + }); + } + catch (error) { + (0, Logger_1.logError)('Validation middleware error', error, req, res); + ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res); + } + }; + runNext(); + }; + } + /** + * Helper method to get nested values from request + * @param req Request object + * @param path Dot-notation path like 'body.user.id' + */ + static getNestedValue(req, path) { + const parts = path.split('.'); + let current = req; + for (const part of parts) { + if (current && typeof current === 'object') { + current = current[part]; + } + else { + return undefined; + } + } + return current; + } +} +exports.ValidationMiddleware = ValidationMiddleware; +//# sourceMappingURL=ValidationMiddleware.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js.map b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js.map new file mode 100644 index 00000000..33112c86 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ValidationMiddleware.js","sourceRoot":"","sources":["../../../src/Application/Services/ValidationMiddleware.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/WebSocketService.d.ts b/SerpentRace_Backend/dist/Application/Services/WebSocketService.d.ts new file mode 100644 index 00000000..ecedf33c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/WebSocketService.d.ts @@ -0,0 +1,66 @@ +import { Server as HttpServer } from 'http'; +import { ChatAggregate } from '../../Domain/Chat/ChatAggregate'; +export declare class WebSocketService { + private io; + private jwtService; + private chatRepository; + private chatArchiveRepository; + private userRepository; + private redisService; + private connectedUsers; + private chatTimeout; + private maxMessagesPerUser; + private messageCleanupWeeks; + private userMessageCounts; + constructor(httpServer: HttpServer); + private initializeRedis; + private setupSocketHandlers; + private handleConnection; + private handleJoinChat; + private handleLeaveChat; + private handleSendMessage; + private handleCreateGroup; + private handleCreateDirectChat; + private handleCreateGameChat; + private handleGetChatHistory; + private handleDeleteChat; + private handleDeleteChatArchive; + private handleDeleteMessage; + private handleDisconnection; + private calculateUnreadMessages; + private pruneMessages; + private notifyOfflineUsers; + private setupArchivingScheduler; + createGameChat(gameId: string, gameName: string, playerIds: string[]): Promise; + getConnectedUserCount(): number; + isUserConnected(userId: string): boolean; + cleanup(): Promise; + /** + * Manually trigger cleanup of old messages and chats + * This can be called by admin endpoints for maintenance + */ + triggerManualCleanup(): Promise<{ + deletedArchives: number; + deletedChats: number; + }>; + /** + * Clean up old messages from archived chats based on messageCleanupWeeks setting + */ + private cleanupOldMessages; + /** + * Check if user has exceeded message rate limit + * @param userId User ID to check + * @returns true if within limit, false if exceeded + */ + private checkMessageRateLimit; + /** + * Delete a specific message from chat history + * This can be used for moderation purposes + */ + deleteMessage(chatId: string, messageId: string, moderatorUserId: string): Promise; + /** + * Clean up old user message count entries (called periodically) + */ + private cleanupMessageCounts; +} +//# sourceMappingURL=WebSocketService.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/WebSocketService.d.ts.map b/SerpentRace_Backend/dist/Application/Services/WebSocketService.d.ts.map new file mode 100644 index 00000000..403b0ae8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/WebSocketService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"WebSocketService.d.ts","sourceRoot":"","sources":["../../../src/Application/Services/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAM5C,OAAO,EAAE,aAAa,EAAmC,MAAM,iCAAiC,CAAC;AAkDjG,qBAAa,gBAAgB;IACzB,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,qBAAqB,CAAwB;IACrD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,iBAAiB,CAAgE;gBAE7E,UAAU,EAAE,UAAU;YA6BpB,eAAe;IAQ7B,OAAO,CAAC,mBAAmB;YA4Db,gBAAgB;YA2EhB,cAAc;YA4Cd,eAAe;YAsBf,iBAAiB;YAsEjB,iBAAiB;YAwFjB,sBAAsB;YA4EtB,oBAAoB;YAsEpB,oBAAoB;YA4CpB,gBAAgB;YA2DhB,uBAAuB;YAqCvB,mBAAmB;YA4BnB,mBAAmB;IAoBjC,OAAO,CAAC,uBAAuB;IAM/B,OAAO,CAAC,aAAa;YAgCP,kBAAkB;IAgBhC,OAAO,CAAC,uBAAuB;IA8DlB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAyC1G,qBAAqB,IAAI,MAAM;IAI/B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrC;;;OAGG;IACU,oBAAoB,IAAI,OAAO,CAAC;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAmC/F;;OAEG;YACW,kBAAkB;IAiChC;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAwB7B;;;OAGG;IACU,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiFxG;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAU/B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/WebSocketService.js b/SerpentRace_Backend/dist/Application/Services/WebSocketService.js new file mode 100644 index 00000000..8d19ccfc --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/WebSocketService.js @@ -0,0 +1,966 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WebSocketService = void 0; +const socket_io_1 = require("socket.io"); +const JWTService_1 = require("./JWTService"); +const ChatRepository_1 = require("../../Infrastructure/Repository/ChatRepository"); +const ChatArchiveRepository_1 = require("../../Infrastructure/Repository/ChatArchiveRepository"); +const UserRepository_1 = require("../../Infrastructure/Repository/UserRepository"); +const ChatAggregate_1 = require("../../Domain/Chat/ChatAggregate"); +const UserAggregate_1 = require("../../Domain/User/UserAggregate"); +const Logger_1 = require("./Logger"); +const RedisService_1 = require("./RedisService"); +const uuid_1 = require("uuid"); +class WebSocketService { + constructor(httpServer) { + this.connectedUsers = new Map(); + this.userMessageCounts = new Map(); + this.io = new socket_io_1.Server(httpServer, { + cors: { + origin: ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:8080'], + methods: ['GET', 'POST'], + credentials: true + } + }); + this.jwtService = new JWTService_1.JWTService(); + this.chatRepository = new ChatRepository_1.ChatRepository(); + this.chatArchiveRepository = new ChatArchiveRepository_1.ChatArchiveRepository(); + this.userRepository = new UserRepository_1.UserRepository(); + this.redisService = RedisService_1.RedisService.getInstance(); + this.chatTimeout = parseInt(process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30'); + this.maxMessagesPerUser = parseInt(process.env.CHAT_MAX_MESSAGES_PER_USER || '100'); + this.messageCleanupWeeks = parseInt(process.env.CHAT_MESSAGE_CLEANUP_WEEKS || '4'); + // Initialize Redis connection + this.initializeRedis(); + this.setupSocketHandlers(); + this.setupArchivingScheduler(); + (0, Logger_1.logRequest)('WebSocket service initialized', undefined, undefined, { + chatTimeoutMinutes: this.chatTimeout + }); + } + async initializeRedis() { + try { + await this.redisService.connect(); + } + catch (error) { + (0, Logger_1.logError)('Failed to initialize Redis connection', error); + } + } + setupSocketHandlers() { + this.io.use(async (socket, next) => { + try { + const token = socket.handshake.auth.token || socket.handshake.headers.cookie + ?.split(';') + .find(c => c.trim().startsWith('auth_token=')) + ?.split('=')[1]; + if (!token) { + (0, Logger_1.logWarning)('WebSocket connection rejected - No token provided', { + socketId: socket.id, + ip: socket.handshake.address + }); + return next(new Error('Authentication required')); + } + // Create a mock request object for JWT verification + const mockRequest = { + headers: { + authorization: `Bearer ${token}`, + cookie: `auth_token=${token}` + }, + cookies: { + auth_token: token + } + }; + const payload = this.jwtService.verify(mockRequest); + if (!payload) { + (0, Logger_1.logWarning)('WebSocket connection rejected - Invalid token', { + socketId: socket.id, + ip: socket.handshake.address + }); + return next(new Error('Invalid token')); + } + socket.userId = payload.userId; + socket.authLevel = payload.authLevel; + socket.userStatus = payload.userStatus; + socket.orgId = payload.orgId; + (0, Logger_1.logAuth)('WebSocket connection authenticated', payload.userId, { + socketId: socket.id, + authLevel: payload.authLevel, + userStatus: payload.userStatus, + orgId: payload.orgId + }); + next(); + } + catch (error) { + (0, Logger_1.logError)('WebSocket authentication error', error); + next(new Error('Authentication failed')); + } + }); + this.io.on('connection', (socket) => { + this.handleConnection(socket); + }); + } + async handleConnection(socket) { + const userId = socket.userId; + // Store connected user + this.connectedUsers.set(userId, socket); + // Load user's active chats and join rooms + try { + const userChats = await this.chatRepository.findActiveChatsForUser(userId); + const chatIds = userChats.map(chat => chat.id); + // Join all chat rooms + chatIds.forEach(chatId => { + socket.join(chatId); + }); + // Store user's chat memberships in Redis + await this.redisService.setActiveUser(userId, { + userId, + activeChatIds: chatIds, + lastActivity: new Date(), + isOnline: true + }); + // Also store each active chat in Redis + for (const chat of userChats) { + await this.redisService.setActiveChat(chat.id, { + chatId: chat.id, + participants: chat.users, + lastActivity: chat.lastActivity || new Date(), + messageCount: chat.messages.length, + chatType: chat.type, + gameId: chat.gameId || undefined, + name: chat.name || undefined + }); + } + (0, Logger_1.logAuth)('User connected to WebSocket', userId, { + socketId: socket.id, + activeChats: chatIds.length + }); + // Send user their active chats with unread counts + const chatsWithUnread = await Promise.all(userChats.map(async (chat) => ({ + id: chat.id, + type: chat.type, + name: chat.name, + gameId: chat.gameId, + users: chat.users, + lastActivity: chat.lastActivity, + unreadCount: this.calculateUnreadMessages(chat, userId), + isArchived: false + }))); + socket.emit('chats:list', chatsWithUnread); + } + catch (error) { + (0, Logger_1.logError)('Error loading user chats on connection', error, undefined, undefined); + socket.emit('error', { message: 'Failed to load chats' }); + } + // Setup event handlers + socket.on('chat:join', (data) => this.handleJoinChat(socket, data)); + socket.on('chat:leave', (data) => this.handleLeaveChat(socket, data)); + socket.on('message:send', (data) => this.handleSendMessage(socket, data)); + socket.on('group:create', (data) => this.handleCreateGroup(socket, data)); + socket.on('chat:direct', (data) => this.handleCreateDirectChat(socket, data)); + socket.on('game:chat:create', (data) => this.handleCreateGameChat(socket, data)); + socket.on('chat:history', (data) => this.handleGetChatHistory(socket, data)); + socket.on('chat:delete', (data) => this.handleDeleteChat(socket, data)); + socket.on('chat:archive:delete', (data) => this.handleDeleteChatArchive(socket, data)); + socket.on('message:delete', (data) => this.handleDeleteMessage(socket, data)); + socket.on('disconnect', () => this.handleDisconnection(socket)); + } + async handleJoinChat(socket, data) { + try { + const userId = socket.userId; + const chat = await this.chatRepository.findById(data.chatId); + if (!chat) { + socket.emit('error', { message: 'Chat not found' }); + return; + } + // Check if user is member of this chat + if (!chat.users.includes(userId)) { + socket.emit('error', { message: 'Unauthorized to join this chat' }); + return; + } + // Join the chat room + socket.join(data.chatId); + // Add to user's active chats in Redis + await this.redisService.addUserToChat(userId, data.chatId); + // Update chat activity in Redis + await this.redisService.updateChatActivity(data.chatId); + // Update last activity in database + await this.chatRepository.update(data.chatId, { lastActivity: new Date() }); + (0, Logger_1.logAuth)('User joined chat', userId, { + chatId: data.chatId, + chatType: chat.type + }); + socket.emit('chat:joined', { + chatId: data.chatId, + messages: chat.messages.slice(-10) // Last 10 messages + }); + } + catch (error) { + (0, Logger_1.logError)('Error joining chat', error); + socket.emit('error', { message: 'Failed to join chat' }); + } + } + async handleLeaveChat(socket, data) { + try { + const userId = socket.userId; + // Leave the chat room + socket.leave(data.chatId); + // Remove from user's active chats in Redis + await this.redisService.removeUserFromChat(userId, data.chatId); + (0, Logger_1.logAuth)('User left chat', userId, { + chatId: data.chatId + }); + socket.emit('chat:left', { chatId: data.chatId }); + } + catch (error) { + (0, Logger_1.logError)('Error leaving chat', error); + socket.emit('error', { message: 'Failed to leave chat' }); + } + } + async handleSendMessage(socket, data) { + try { + const userId = socket.userId; + // Rate limiting check + if (!this.checkMessageRateLimit(userId)) { + socket.emit('error', { message: `Rate limit exceeded. Maximum ${this.maxMessagesPerUser} messages per minute allowed.` }); + return; + } + // Validate message is string and not empty + if (typeof data.message !== 'string' || !data.message.trim()) { + socket.emit('error', { message: 'Message must be a non-empty string' }); + return; + } + const chat = await this.chatRepository.findById(data.chatId); + if (!chat) { + socket.emit('error', { message: 'Chat not found' }); + return; + } + // Check if user is member of this chat + if (!chat.users.includes(userId)) { + socket.emit('error', { message: 'Unauthorized to send message to this chat' }); + return; + } + // Create message + const message = { + id: (0, uuid_1.v4)(), + date: new Date(), + userid: userId, + text: data.message.trim() + }; + // Manage message history based on chat type + let updatedMessages = [...chat.messages, message]; + updatedMessages = this.pruneMessages(updatedMessages, chat.type); + // Update chat + await this.chatRepository.update(data.chatId, { + messages: updatedMessages, + lastActivity: new Date() + }); + // Update chat activity in Redis with new message count + await this.redisService.updateChatActivity(data.chatId, updatedMessages.length); + // Broadcast to all users in the chat room + this.io.to(data.chatId).emit('message:received', { + chatId: data.chatId, + message: message + }); + // Send notifications to offline users + await this.notifyOfflineUsers(chat, message); + (0, Logger_1.logAuth)('Message sent', userId, { + chatId: data.chatId, + messageLength: data.message.length, + chatType: chat.type + }); + } + catch (error) { + (0, Logger_1.logError)('Error sending message', error); + socket.emit('error', { message: 'Failed to send message' }); + } + } + async handleCreateGroup(socket, data) { + try { + const userId = socket.userId; + // Check if user is premium (required to create groups) + const user = await this.userRepository.findById(userId); + if (!user || user.state !== UserAggregate_1.UserState.VERIFIED_PREMIUM) { + socket.emit('error', { message: 'Premium subscription required to create groups' }); + return; + } + // Validate group data + if (!data.name?.trim()) { + socket.emit('error', { message: 'Group name is required' }); + return; + } + if (!data.userIds || data.userIds.length === 0) { + socket.emit('error', { message: 'At least one member is required' }); + return; + } + // Verify all users exist + const members = await Promise.all(data.userIds.map(id => this.userRepository.findById(id))); + if (members.some(member => !member)) { + socket.emit('error', { message: 'One or more users not found' }); + return; + } + // Create group chat + const groupChat = await this.chatRepository.create({ + type: ChatAggregate_1.ChatType.GROUP, + name: data.name.trim(), + createdBy: userId, + users: [userId, ...data.userIds], // Include creator + messages: [], + lastActivity: new Date() + }); + // Add all members to the group room and store in Redis + const allMemberIds = data.userIds.concat(userId); + for (const memberId of allMemberIds) { + const memberSocket = this.connectedUsers.get(memberId); + if (memberSocket) { + memberSocket.join(groupChat.id); + } + // Update user's chat list in Redis + await this.redisService.addUserToChat(memberId, groupChat.id); + } + // Store the group chat in Redis + await this.redisService.setActiveChat(groupChat.id, { + chatId: groupChat.id, + participants: allMemberIds, + lastActivity: new Date(), + messageCount: 0, + chatType: 'group', + name: groupChat.name || undefined + }); + // Notify all members + this.io.to(groupChat.id).emit('group:created', { + chat: { + id: groupChat.id, + type: groupChat.type, + name: groupChat.name, + createdBy: groupChat.createdBy, + users: groupChat.users, + messages: [] + } + }); + (0, Logger_1.logAuth)('Group created', userId, { + groupId: groupChat.id, + groupName: data.name, + memberCount: groupChat.users.length + }); + } + catch (error) { + (0, Logger_1.logError)('Error creating group', error); + socket.emit('error', { message: 'Failed to create group' }); + } + } + async handleCreateDirectChat(socket, data) { + try { + const userId = socket.userId; + // Validate target user exists + const targetUser = await this.userRepository.findById(data.targetUserId); + if (!targetUser) { + socket.emit('error', { message: 'Target user not found' }); + return; + } + // Check if direct chat already exists + const existingChats = await this.chatRepository.findByUserId(userId); + const existingDirectChat = existingChats.find(chat => chat.type === ChatAggregate_1.ChatType.DIRECT && + chat.users.length === 2 && + chat.users.includes(data.targetUserId)); + if (existingDirectChat) { + socket.emit('chat:direct:exists', { + chatId: existingDirectChat.id + }); + return; + } + // Create direct chat + const directChat = await this.chatRepository.create({ + type: ChatAggregate_1.ChatType.DIRECT, + users: [userId, data.targetUserId], + messages: [], + lastActivity: new Date() + }); + // Add both users to the chat room if they're online and store in Redis + const memberIds = [userId, data.targetUserId]; + for (const memberId of memberIds) { + const memberSocket = this.connectedUsers.get(memberId); + if (memberSocket) { + memberSocket.join(directChat.id); + } + // Update user's chat list in Redis + await this.redisService.addUserToChat(memberId, directChat.id); + } + // Store the direct chat in Redis + await this.redisService.setActiveChat(directChat.id, { + chatId: directChat.id, + participants: memberIds, + lastActivity: new Date(), + messageCount: 0, + chatType: 'direct' + }); + // Notify both users + this.io.to(directChat.id).emit('chat:direct:created', { + chat: { + id: directChat.id, + type: directChat.type, + users: directChat.users, + messages: [] + } + }); + (0, Logger_1.logAuth)('Direct chat created', userId, { + chatId: directChat.id, + targetUserId: data.targetUserId + }); + } + catch (error) { + (0, Logger_1.logError)('Error creating direct chat', error); + socket.emit('error', { message: 'Failed to create direct chat' }); + } + } + async handleCreateGameChat(socket, data) { + try { + const userId = socket.userId; + // Check if game chat already exists + const existingGameChat = await this.chatRepository.findByGameId(data.gameId); + if (existingGameChat) { + socket.emit('game:chat:exists', { + chatId: existingGameChat.id + }); + return; + } + // Create game chat + const gameChat = await this.chatRepository.create({ + type: ChatAggregate_1.ChatType.GAME, + name: data.gameName, + gameId: data.gameId, + users: data.playerIds, + messages: [], + lastActivity: new Date() + }); + // Add all players to the game chat room if they're online and store in Redis + for (const playerId of data.playerIds) { + const playerSocket = this.connectedUsers.get(playerId); + if (playerSocket) { + playerSocket.join(gameChat.id); + } + // Update user's chat list in Redis + await this.redisService.addUserToChat(playerId, gameChat.id); + } + // Store the game chat in Redis + await this.redisService.setActiveChat(gameChat.id, { + chatId: gameChat.id, + participants: data.playerIds, + lastActivity: new Date(), + messageCount: 0, + chatType: 'game', + gameId: gameChat.gameId || undefined, + name: gameChat.name || undefined + }); + // Notify all players + this.io.to(gameChat.id).emit('game:chat:created', { + chat: { + id: gameChat.id, + type: gameChat.type, + name: gameChat.name, + gameId: gameChat.gameId, + users: gameChat.users, + messages: [] + } + }); + (0, Logger_1.logAuth)('Game chat created', userId, { + chatId: gameChat.id, + gameId: data.gameId, + gameName: data.gameName, + playerCount: data.playerIds.length + }); + } + catch (error) { + (0, Logger_1.logError)('Error creating game chat', error); + socket.emit('error', { message: 'Failed to create game chat' }); + } + } + async handleGetChatHistory(socket, data) { + try { + const userId = socket.userId; + const chat = await this.chatRepository.findById(data.chatId); + if (!chat) { + // Check if it's archived + const archived = await this.chatRepository.getArchivedChat(data.chatId); + if (archived) { + socket.emit('chat:history:archived', { + chatId: data.chatId, + messages: archived.archivedMessages, + chatType: archived.chatType, + isGameChat: archived.chatType === ChatAggregate_1.ChatType.GAME + }); + } + else { + socket.emit('error', { message: 'Chat not found' }); + } + return; + } + // Check if user has access + if (!chat.users.includes(userId)) { + socket.emit('error', { message: 'Unauthorized to view this chat' }); + return; + } + socket.emit('chat:history', { + chatId: data.chatId, + messages: chat.messages, + chatInfo: { + type: chat.type, + name: chat.name, + gameId: chat.gameId, + users: chat.users + } + }); + } + catch (error) { + (0, Logger_1.logError)('Error getting chat history', error); + socket.emit('error', { message: 'Failed to get chat history' }); + } + } + async handleDeleteChat(socket, data) { + try { + const userId = socket.userId; + const chat = await this.chatRepository.findById(data.chatId); + if (!chat) { + socket.emit('error', { message: 'Chat not found' }); + return; + } + // Check if user is member of this chat + if (!chat.users.includes(userId)) { + socket.emit('error', { message: 'Unauthorized to delete this chat' }); + return; + } + // Perform soft delete + const deletedChat = await this.chatRepository.softDelete(data.chatId); + if (!deletedChat) { + socket.emit('error', { message: 'Failed to delete chat' }); + return; + } + // Remove from Redis active chats + await this.redisService.removeActiveChat(data.chatId); + // Notify all participants that the chat has been deleted + this.io.to(data.chatId).emit('chat:deleted', { + chatId: data.chatId, + deletedBy: userId + }); + // Remove all users from the chat room + for (const participantId of chat.users) { + const participantSocket = this.connectedUsers.get(participantId); + if (participantSocket) { + participantSocket.leave(data.chatId); + } + // Remove from user's active chats in Redis + await this.redisService.removeUserFromChat(participantId, data.chatId); + } + (0, Logger_1.logAuth)('Chat deleted', userId, { + chatId: data.chatId, + chatType: chat.type, + participantCount: chat.users.length + }); + socket.emit('chat:delete:success', { + chatId: data.chatId, + message: 'Chat deleted successfully' + }); + } + catch (error) { + (0, Logger_1.logError)('Error deleting chat', error); + socket.emit('error', { message: 'Failed to delete chat' }); + } + } + async handleDeleteChatArchive(socket, data) { + try { + const userId = socket.userId; + const archive = await this.chatArchiveRepository.findById(data.archiveId); + if (!archive) { + socket.emit('error', { message: 'Chat archive not found' }); + return; + } + // Check if user was a participant in the archived chat + if (!archive.participants.includes(userId)) { + socket.emit('error', { message: 'Unauthorized to delete this chat archive' }); + return; + } + // Hard delete the archive (since it's already archived) + await this.chatArchiveRepository.delete(data.archiveId); + (0, Logger_1.logAuth)('Chat archive deleted', userId, { + archiveId: data.archiveId, + originalChatId: archive.chatId, + chatType: archive.chatType, + participantCount: archive.participants.length + }); + socket.emit('chat:archive:delete:success', { + archiveId: data.archiveId, + message: 'Chat archive deleted successfully' + }); + } + catch (error) { + (0, Logger_1.logError)('Error deleting chat archive', error); + socket.emit('error', { message: 'Failed to delete chat archive' }); + } + } + async handleDeleteMessage(socket, data) { + try { + const userId = socket.userId; + // Check if user has admin/moderator privileges + const user = await this.userRepository.findById(userId); + if (!user || user.state !== UserAggregate_1.UserState.ADMIN) { // Check if user is admin + socket.emit('error', { message: 'Insufficient permissions to delete messages' }); + return; + } + const success = await this.deleteMessage(data.chatId, data.messageId, userId); + if (success) { + socket.emit('message:delete:success', { + chatId: data.chatId, + messageId: data.messageId, + message: 'Message deleted successfully' + }); + } + else { + socket.emit('error', { message: 'Failed to delete message or message not found' }); + } + } + catch (error) { + (0, Logger_1.logError)('Error handling delete message request', error); + socket.emit('error', { message: 'Failed to delete message' }); + } + } + async handleDisconnection(socket) { + const userId = socket.userId; + if (userId) { + this.connectedUsers.delete(userId); + // Update user status in Redis + const userData = await this.redisService.getActiveUser(userId); + if (userData) { + userData.isOnline = false; + userData.lastActivity = new Date(); + await this.redisService.setActiveUser(userId, userData); + } + (0, Logger_1.logAuth)('User disconnected from WebSocket', userId, { + socketId: socket.id + }); + } + } + // Utility methods + calculateUnreadMessages(chat, userId) { + // Simple implementation - count messages after user's last seen + // In production, you'd store lastSeen timestamp per user per chat + return chat.messages.filter(msg => msg.userid !== userId).length; + } + pruneMessages(messages, chatType) { + const twoWeeksAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000); + // Remove messages older than 2 weeks + let prunedMessages = messages.filter(msg => new Date(msg.date) > twoWeeksAgo); + // For group chats, only apply the 2-week time limit (unlimited messages per user) + if (chatType === ChatAggregate_1.ChatType.GROUP) { + return prunedMessages.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + } + // For direct and game chats, apply both time limit and per-user message limit + // Group by user and keep last 10 messages per user + const messagesByUser = new Map(); + prunedMessages.forEach(msg => { + if (!messagesByUser.has(msg.userid)) { + messagesByUser.set(msg.userid, []); + } + messagesByUser.get(msg.userid).push(msg); + }); + // Keep only last 10 messages per user + const finalMessages = []; + messagesByUser.forEach((userMessages, userId) => { + const last10 = userMessages.slice(-10); + finalMessages.push(...last10); + }); + // Sort by date + return finalMessages.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + } + async notifyOfflineUsers(chat, message) { + // Find users who are not currently connected + const offlineUsers = chat.users.filter(userId => userId !== message.userid && !this.connectedUsers.has(userId)); + // In a real implementation, you would send push notifications or emails here + if (offlineUsers.length > 0) { + (0, Logger_1.logRequest)('Offline users to notify', undefined, undefined, { + chatId: chat.id, + offlineUserCount: offlineUsers.length, + messageFrom: message.userid + }); + } + } + setupArchivingScheduler() { + // Run every hour to check for inactive chats + setInterval(async () => { + try { + // First, cleanup inactive chats from Redis and get their IDs + const inactiveChatIds = await this.redisService.cleanupInactiveChats(this.chatTimeout); + // Archive the inactive chats in the database + for (const chatId of inactiveChatIds) { + const chat = await this.chatRepository.findById(chatId); + if (chat) { + await this.chatRepository.archiveChat(chat); + (0, Logger_1.logRequest)('Chat archived due to inactivity', undefined, undefined, { + chatId: chat.id, + chatType: chat.type, + lastActivity: chat.lastActivity, + messageCount: chat.messages.length + }); + } + } + // Also find inactive chats from database that might not be in Redis + const dbInactiveChats = await this.chatRepository.findInactiveChats(this.chatTimeout); + const additionalInactiveChats = dbInactiveChats.filter(chat => !inactiveChatIds.includes(chat.id)); + for (const chat of additionalInactiveChats) { + await this.chatRepository.archiveChat(chat); + (0, Logger_1.logRequest)('Chat archived due to inactivity (from DB)', undefined, undefined, { + chatId: chat.id, + chatType: chat.type, + lastActivity: chat.lastActivity, + messageCount: chat.messages.length + }); + } + const totalArchived = inactiveChatIds.length + additionalInactiveChats.length; + if (totalArchived > 0) { + (0, Logger_1.logRequest)('Chat archiving completed', undefined, undefined, { + archivedCount: totalArchived, + redisCleanedUp: inactiveChatIds.length, + databaseCleanedUp: additionalInactiveChats.length, + timeoutMinutes: this.chatTimeout + }); + } + // Cleanup old messages from archived chats based on messageCleanupWeeks + await this.cleanupOldMessages(); + } + catch (error) { + (0, Logger_1.logError)('Error in chat archiving scheduler', error); + } + }, 60 * 60 * 1000); // 1 hour + // Also run message count cleanup every 5 minutes + setInterval(() => { + this.cleanupMessageCounts(); + }, 5 * 60 * 1000); // 5 minutes + } + // Public methods for game integration + async createGameChat(gameId, gameName, playerIds) { + try { + const existingGameChat = await this.chatRepository.findByGameId(gameId); + if (existingGameChat) { + return existingGameChat; + } + const gameChat = await this.chatRepository.create({ + type: ChatAggregate_1.ChatType.GAME, + name: gameName, + gameId: gameId, + users: playerIds, + messages: [], + lastActivity: new Date() + }); + // Notify connected players + playerIds.forEach(playerId => { + const playerSocket = this.connectedUsers.get(playerId); + if (playerSocket) { + playerSocket.join(gameChat.id); + playerSocket.emit('game:chat:created', { + chat: { + id: gameChat.id, + type: gameChat.type, + name: gameChat.name, + gameId: gameChat.gameId, + users: gameChat.users, + messages: [] + } + }); + } + }); + return gameChat; + } + catch (error) { + (0, Logger_1.logError)('Error creating game chat programmatically', error); + return null; + } + } + getConnectedUserCount() { + return this.connectedUsers.size; + } + isUserConnected(userId) { + return this.connectedUsers.has(userId); + } + async cleanup() { + try { + await this.redisService.disconnect(); + } + catch (error) { + (0, Logger_1.logError)('Error during WebSocket service cleanup', error); + } + } + /** + * Manually trigger cleanup of old messages and chats + * This can be called by admin endpoints for maintenance + */ + async triggerManualCleanup() { + try { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - (this.messageCleanupWeeks * 7)); + // Clean up old archived messages + const deletedArchivesCount = await this.chatArchiveRepository.cleanup(this.messageCleanupWeeks * 7); + // Clean up soft-deleted chats + const softDeletedChats = await this.chatRepository.findByPageIncludingDeleted(0, 1000); + let deletedChatsCount = 0; + for (const chat of softDeletedChats.chats) { + if (chat.state === 2 && chat.updateDate < cutoffDate) { // SOFT_DELETE state = 2 + await this.chatRepository.delete(chat.id); // Hard delete + deletedChatsCount++; + } + } + (0, Logger_1.logRequest)('Manual cleanup triggered', undefined, undefined, { + cutoffDate: cutoffDate.toISOString(), + cleanupWeeks: this.messageCleanupWeeks, + deletedArchives: deletedArchivesCount, + deletedChats: deletedChatsCount, + triggeredBy: 'manual' + }); + return { deletedArchives: deletedArchivesCount, deletedChats: deletedChatsCount }; + } + catch (error) { + (0, Logger_1.logError)('Error during manual cleanup', error); + throw error; + } + } + /** + * Clean up old messages from archived chats based on messageCleanupWeeks setting + */ + async cleanupOldMessages() { + try { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - (this.messageCleanupWeeks * 7)); + // Clean up old archived messages using ChatArchiveRepository + const deletedArchivesCount = await this.chatArchiveRepository.cleanup(this.messageCleanupWeeks * 7); + // Also clean up soft-deleted chats from the main repository + // Get all soft-deleted chats that are older than the cleanup period + const softDeletedChats = await this.chatRepository.findByPageIncludingDeleted(0, 1000); + let deletedChatsCount = 0; + for (const chat of softDeletedChats.chats) { + if (chat.state === 2 && chat.updateDate < cutoffDate) { // SOFT_DELETE state = 2 + await this.chatRepository.delete(chat.id); // Hard delete + deletedChatsCount++; + } + } + (0, Logger_1.logRequest)('Old message cleanup completed', undefined, undefined, { + cutoffDate: cutoffDate.toISOString(), + cleanupWeeks: this.messageCleanupWeeks, + deletedArchives: deletedArchivesCount, + deletedChats: deletedChatsCount, + note: 'Cleanup completed using both ChatRepository and ChatArchiveRepository' + }); + } + catch (error) { + (0, Logger_1.logError)('Error cleaning up old messages', error); + } + } + /** + * Check if user has exceeded message rate limit + * @param userId User ID to check + * @returns true if within limit, false if exceeded + */ + checkMessageRateLimit(userId) { + const now = Date.now(); + const minute = 60 * 1000; // 1 minute in milliseconds + const userStats = this.userMessageCounts.get(userId) || { count: 0, lastReset: now }; + // Reset counter if more than a minute has passed + if (now - userStats.lastReset >= minute) { + userStats.count = 0; + userStats.lastReset = now; + } + // Check if user is within limits + if (userStats.count >= this.maxMessagesPerUser) { + return false; + } + // Increment counter + userStats.count++; + this.userMessageCounts.set(userId, userStats); + return true; + } + /** + * Delete a specific message from chat history + * This can be used for moderation purposes + */ + async deleteMessage(chatId, messageId, moderatorUserId) { + try { + // Get the chat + const chat = await this.chatRepository.findById(chatId); + if (!chat) { + // Check archived chats + const archivedChat = await this.chatRepository.getArchivedChat(chatId); + if (!archivedChat) { + (0, Logger_1.logWarning)('Chat not found for message deletion', { + chatId, + messageId, + moderatorUserId + }); + return false; + } + // Remove message from archived chat + const updatedMessages = archivedChat.archivedMessages.filter(msg => msg.id !== messageId); + if (updatedMessages.length === archivedChat.archivedMessages.length) { + (0, Logger_1.logWarning)('Message not found in archived chat', { + chatId, + messageId, + moderatorUserId + }); + return false; + } + // Update archived chat + await this.chatArchiveRepository.create({ + ...archivedChat, + archivedMessages: updatedMessages + }); + (0, Logger_1.logAuth)('Message deleted from archived chat', moderatorUserId, { + chatId, + messageId, + originalMessageCount: archivedChat.archivedMessages.length, + newMessageCount: updatedMessages.length + }); + return true; + } + // Remove message from active chat + const updatedMessages = chat.messages.filter(msg => msg.id !== messageId); + if (updatedMessages.length === chat.messages.length) { + (0, Logger_1.logWarning)('Message not found in active chat', { + chatId, + messageId, + moderatorUserId + }); + return false; + } + // Update active chat + await this.chatRepository.update(chatId, { + messages: updatedMessages + }); + // Notify all users in the chat about message deletion + this.io.to(chatId).emit('message:deleted', { + chatId, + messageId, + deletedBy: moderatorUserId + }); + (0, Logger_1.logAuth)('Message deleted from active chat', moderatorUserId, { + chatId, + messageId, + originalMessageCount: chat.messages.length, + newMessageCount: updatedMessages.length + }); + return true; + } + catch (error) { + (0, Logger_1.logError)('Error deleting message', error); + return false; + } + } + /** + * Clean up old user message count entries (called periodically) + */ + cleanupMessageCounts() { + const now = Date.now(); + const minute = 60 * 1000; + for (const [userId, stats] of this.userMessageCounts.entries()) { + if (now - stats.lastReset >= minute * 5) { // Keep for 5 minutes + this.userMessageCounts.delete(userId); + } + } + } +} +exports.WebSocketService = WebSocketService; +//# sourceMappingURL=WebSocketService.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/Services/WebSocketService.js.map b/SerpentRace_Backend/dist/Application/Services/WebSocketService.js.map new file mode 100644 index 00000000..6c38c4c1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/Services/WebSocketService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WebSocketService.js","sourceRoot":"","sources":["../../../src/Application/Services/WebSocketService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.d.ts new file mode 100644 index 00000000..f4603b43 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.d.ts @@ -0,0 +1,12 @@ +export interface CreateUserCommand { + username: string; + password: string; + email: string; + fname: string; + lname: string; + code?: string; + orgid?: string; + type: string; + phone?: string; +} +//# sourceMappingURL=CreateUserCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.d.ts.map new file mode 100644 index 00000000..01e6f1dc --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateUserCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/CreateUserCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.js b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.js new file mode 100644 index 00000000..22c16988 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=CreateUserCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.js.map new file mode 100644 index 00000000..ef4224fe --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateUserCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/CreateUserCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.d.ts new file mode 100644 index 00000000..159c4773 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.d.ts @@ -0,0 +1,10 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { CreateUserCommand } from './CreateUserCommand'; +import { ShortUserDto } from '../../DTOs/UserDto'; +export declare class CreateUserCommandHandler { + private readonly userRepo; + private emailService; + constructor(userRepo: IUserRepository); + execute(cmd: CreateUserCommand): Promise; +} +//# sourceMappingURL=CreateUserCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.d.ts.map new file mode 100644 index 00000000..67ddda54 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateUserCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/CreateUserCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,qBAAa,wBAAwB;IAGvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAFrC,OAAO,CAAC,YAAY,CAAe;gBAEN,QAAQ,EAAE,eAAe;IAIhD,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;CAuE7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.js new file mode 100644 index 00000000..67ebc941 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.js @@ -0,0 +1,74 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateUserCommandHandler = void 0; +const UserAggregate_1 = require("../../../Domain/User/UserAggregate"); +const UserMapper_1 = require("../../DTOs/Mappers/UserMapper"); +const PasswordService_1 = require("../../Services/PasswordService"); +const EmailService_1 = require("../../Services/EmailService"); +const TokenService_1 = require("../../Services/TokenService"); +const Logger_1 = require("../../Services/Logger"); +class CreateUserCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + this.emailService = new EmailService_1.EmailService(); + } + async execute(cmd) { + try { + // Validate password strength + const passwordValidation = PasswordService_1.PasswordService.validatePasswordStrength(cmd.password); + if (!passwordValidation.isValid) { + throw new Error(`Password validation failed: ${passwordValidation.errors.join(', ')}`); + } + const user = new UserAggregate_1.UserAggregate(); + user.username = cmd.username; + // Hash the password before storing + user.password = await PasswordService_1.PasswordService.hashPassword(cmd.password); + // Generate verification token + const verificationTokenData = TokenService_1.TokenService.generateVerificationToken(); + user.token = await TokenService_1.TokenService.hashToken(verificationTokenData.token); + user.TokenExpires = verificationTokenData.expiresAt; + user.email = cmd.email; + user.fname = cmd.fname; + user.lname = cmd.lname; + user.orgid = cmd.orgid || null; + user.token = cmd.code || null; + user.type = cmd.type; + user.phone = cmd.phone || null; + user.state = UserAggregate_1.UserState.REGISTERED_NOT_VERIFIED; + const created = await this.userRepo.create(user); + // Send verification email + try { + const baseUrl = process.env.APP_BASE_URL || 'http://localhost:3000'; + const verificationUrl = TokenService_1.TokenService.generateVerificationUrl(baseUrl, verificationTokenData.token); + const emailSent = await this.emailService.sendVerificationEmail(created.email, `${created.fname} ${created.lname}`, verificationTokenData.token, verificationUrl); + if (!emailSent) { + (0, Logger_1.logWarning)('Failed to send verification email', { email: created.email, userId: created.id }); + // Don't throw error - user creation should still succeed even if email fails + } + else { + (0, Logger_1.logAuth)('Verification email sent successfully', created.id, { email: created.email }); + } + } + catch (emailError) { + (0, Logger_1.logError)('Error sending verification email', emailError); + // Don't throw error - user creation should still succeed even if email fails + } + return UserMapper_1.UserMapper.toShortDto(created); + } + catch (error) { + (0, Logger_1.logError)('CreateUserCommandHandler error', error); + // Re-throw validation errors as-is + if (error instanceof Error && error.message.includes('Password validation failed')) { + throw error; + } + // Handle database constraint errors + if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique'))) { + throw new Error('User with this username or email already exists'); + } + // Generic error for other cases + throw new Error('Failed to create user'); + } + } +} +exports.CreateUserCommandHandler = CreateUserCommandHandler; +//# sourceMappingURL=CreateUserCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.js.map new file mode 100644 index 00000000..77073d8b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/CreateUserCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CreateUserCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/CreateUserCommandHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.d.ts new file mode 100644 index 00000000..cd25211a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.d.ts @@ -0,0 +1,4 @@ +export interface DeactivateUserCommand { + id: string; +} +//# sourceMappingURL=DeactivateUserCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.d.ts.map new file mode 100644 index 00000000..0fdc3524 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeactivateUserCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeactivateUserCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.js b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.js new file mode 100644 index 00000000..3f6302d5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=DeactivateUserCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.js.map new file mode 100644 index 00000000..728a3747 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeactivateUserCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeactivateUserCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.d.ts new file mode 100644 index 00000000..25e0068d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { DeactivateUserCommand } from './DeactivateUserCommand'; +export declare class DeactivateUserCommandHandler { + private readonly userRepo; + constructor(userRepo: IUserRepository); + execute(cmd: DeactivateUserCommand): Promise; +} +//# sourceMappingURL=DeactivateUserCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.d.ts.map new file mode 100644 index 00000000..5d7aa9f1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeactivateUserCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeactivateUserCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,qBAAa,4BAA4B;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC;CAI5D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.js new file mode 100644 index 00000000..d30caa56 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeactivateUserCommandHandler = void 0; +class DeactivateUserCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(cmd) { + await this.userRepo.deactivate(cmd.id); + return true; + } +} +exports.DeactivateUserCommandHandler = DeactivateUserCommandHandler; +//# sourceMappingURL=DeactivateUserCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.js.map new file mode 100644 index 00000000..6c87c0c1 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeactivateUserCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeactivateUserCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeactivateUserCommandHandler.ts"],"names":[],"mappings":";;;AAIA,MAAa,4BAA4B;IACvC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,GAA0B;QACtC,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAPD,oEAOC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.d.ts new file mode 100644 index 00000000..38077b8f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.d.ts @@ -0,0 +1,5 @@ +export interface DeleteUserCommand { + id: string; + soft?: boolean; +} +//# sourceMappingURL=DeleteUserCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.d.ts.map new file mode 100644 index 00000000..a9331a4d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteUserCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeleteUserCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.js b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.js new file mode 100644 index 00000000..6d9f8c49 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=DeleteUserCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.js.map new file mode 100644 index 00000000..96340b7c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteUserCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeleteUserCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.d.ts new file mode 100644 index 00000000..e2b25754 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { DeleteUserCommand } from './DeleteUserCommand'; +export declare class DeleteUserCommandHandler { + private readonly userRepo; + constructor(userRepo: IUserRepository); + execute(cmd: DeleteUserCommand): Promise; +} +//# sourceMappingURL=DeleteUserCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.d.ts.map new file mode 100644 index 00000000..41b3177c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteUserCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeleteUserCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;CAQxD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.js new file mode 100644 index 00000000..cae8f506 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteUserCommandHandler = void 0; +class DeleteUserCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(cmd) { + if (cmd.soft) { + await this.userRepo.softDelete(cmd.id); + } + else { + await this.userRepo.delete(cmd.id); + } + return true; + } +} +exports.DeleteUserCommandHandler = DeleteUserCommandHandler; +//# sourceMappingURL=DeleteUserCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.js.map new file mode 100644 index 00000000..a53a2f77 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/DeleteUserCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeleteUserCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/DeleteUserCommandHandler.ts"],"names":[],"mappings":";;;AAIA,MAAa,wBAAwB;IACnC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,GAAsB;QAClC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAXD,4DAWC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.d.ts new file mode 100644 index 00000000..d9120a59 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.d.ts @@ -0,0 +1,5 @@ +export interface LoginCommand { + username: string; + password: string; +} +//# sourceMappingURL=LoginCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.d.ts.map new file mode 100644 index 00000000..29127c59 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"LoginCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/LoginCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.js b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.js new file mode 100644 index 00000000..1c9c929a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=LoginCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.js.map new file mode 100644 index 00000000..f4f29b5c --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LoginCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/LoginCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.d.ts new file mode 100644 index 00000000..ea1bf42a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.d.ts @@ -0,0 +1,20 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { IOrganizationRepository } from '../../../Domain/IRepository/IOrganizationRepository'; +import { LoginCommand } from './LoginCommand'; +import { ShortUserDto } from '../../DTOs/UserDto'; +import { JWTService } from '../../Services/JWTService'; +export interface LoginResponse { + user: ShortUserDto; + token: string; + requiresOrgReauth?: boolean; + orgLoginUrl?: string; + organizationName?: string; +} +export declare class LoginCommandHandler { + private readonly userRepo; + private readonly jwtService; + private readonly orgRepo; + constructor(userRepo: IUserRepository, jwtService: JWTService, orgRepo: IOrganizationRepository); + execute(cmd: LoginCommand): Promise; +} +//# sourceMappingURL=LoginCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.d.ts.map new file mode 100644 index 00000000..f14bf5f2 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"LoginCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/LoginCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAIvD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,QAAQ,EAAE,eAAe,EACzB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,uBAAuB;IAG7C,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CAkIhE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.js new file mode 100644 index 00000000..ed3f89d8 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.js @@ -0,0 +1,127 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoginCommandHandler = void 0; +const UserMapper_1 = require("../../DTOs/Mappers/UserMapper"); +const PasswordService_1 = require("../../Services/PasswordService"); +const UserAggregate_1 = require("../../../Domain/User/UserAggregate"); +const Logger_1 = require("../../Services/Logger"); +class LoginCommandHandler { + constructor(userRepo, jwtService, orgRepo) { + this.userRepo = userRepo; + this.jwtService = jwtService; + this.orgRepo = orgRepo; + } + async execute(cmd) { + const startTime = Date.now(); + try { + (0, Logger_1.logAuth)('Login attempt', undefined, { username: cmd.username }); + const user = await this.userRepo.findByUsername(cmd.username) || + await this.userRepo.findByEmail(cmd.username); + (0, Logger_1.logDatabase)('User lookup completed', undefined, Date.now() - startTime, { + found: !!user, + searchBy: cmd.username.includes('@') ? 'email' : 'username' + }); + if (!user) { + (0, Logger_1.logAuth)('Login failed - User not found', undefined, { username: cmd.username }); + return null; + } + try { + const passwordStartTime = Date.now(); + const isPasswordValid = await PasswordService_1.PasswordService.verifyPassword(cmd.password, user.password); + (0, Logger_1.logAuth)('Password verification completed', user.id, { + valid: isPasswordValid, + verificationTime: Date.now() - passwordStartTime + }); + if (!isPasswordValid) { + (0, Logger_1.logWarning)('Login failed - Invalid password', { + userId: user.id, + username: cmd.username + }); + return null; + } + } + catch (error) { + (0, Logger_1.logError)('Password verification error', error); + return null; + } + const mockRes = { + cookie: () => { } + }; + const tokenPayload = { + userId: user.id, + authLevel: (user.state === UserAggregate_1.UserState.ADMIN ? 1 : 0), + userStatus: user.state, + orgId: user.orgid || '' + }; + try { + const token = this.jwtService.create(tokenPayload, mockRes); + // Check if user belongs to an organization and needs reauthentication + let requiresOrgReauth = false; + let orgLoginUrl; + let organizationName; + if (user.orgid) { + const organization = await this.orgRepo.findById(user.orgid); + if (organization) { + organizationName = organization.name; + // Check if user has logged in to organization within the last month + const oneMonthAgo = new Date(); + oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); + const needsReauth = !user.Orglogindate || user.Orglogindate < oneMonthAgo; + if (needsReauth && organization.url) { + requiresOrgReauth = true; + orgLoginUrl = organization.url; + (0, Logger_1.logAuth)('User requires organization reauthentication', user.id, { + organizationId: user.orgid, + organizationName: organization.name, + lastOrgLogin: user.Orglogindate?.toISOString() || 'never', + orgLoginUrl: organization.url + }); + } + } + } + (0, Logger_1.logAuth)('Login successful', user.id, { + authLevel: tokenPayload.authLevel, + userStatus: tokenPayload.userStatus, + orgId: tokenPayload.orgId, + requiresOrgReauth, + organizationName, + totalLoginTime: Date.now() - startTime + }); + const response = { + user: UserMapper_1.UserMapper.toShortDto(user), + token + }; + if (requiresOrgReauth) { + response.requiresOrgReauth = true; + response.orgLoginUrl = orgLoginUrl; + response.organizationName = organizationName; + } + return response; + } + catch (error) { + (0, Logger_1.logError)('Token creation failed during login', error); + throw new Error('Login failed due to internal error'); + } + } + catch (error) { + if (error instanceof Error) { + (0, Logger_1.logError)('Login handler error', error); + // Handle database connection errors + if (error.message.includes('database connection')) { + (0, Logger_1.logDatabase)('Database connection error during login', undefined, Date.now() - startTime); + throw new Error('Database connection error'); + } + // If it's already a properly formatted error, re-throw it + if (error.message === 'Login failed due to internal error' || + error.message === 'Database connection error') { + throw error; + } + } + // Default database error handling + (0, Logger_1.logDatabase)('Unexpected database error during login', undefined, Date.now() - startTime); + throw new Error('Database connection error'); + } + } +} +exports.LoginCommandHandler = LoginCommandHandler; +//# sourceMappingURL=LoginCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.js.map new file mode 100644 index 00000000..f6ed5860 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/LoginCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LoginCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/LoginCommandHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.d.ts new file mode 100644 index 00000000..a8ff2460 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.d.ts @@ -0,0 +1,4 @@ +export interface RequestPasswordResetCommand { + email: string; +} +//# sourceMappingURL=RequestPasswordResetCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.d.ts.map new file mode 100644 index 00000000..08afb3cf --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestPasswordResetCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/RequestPasswordResetCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.js b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.js new file mode 100644 index 00000000..619ccf0a --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=RequestPasswordResetCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.js.map new file mode 100644 index 00000000..47fe24dc --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestPasswordResetCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/RequestPasswordResetCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.d.ts new file mode 100644 index 00000000..a1e725a9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { RequestPasswordResetCommand } from './RequestPasswordResetCommand'; +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +export declare class RequestPasswordResetCommandHandler { + private userRepo; + private emailService; + constructor(userRepo: IUserRepository); + execute(cmd: RequestPasswordResetCommand): Promise; +} +//# sourceMappingURL=RequestPasswordResetCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.d.ts.map new file mode 100644 index 00000000..ff82f640 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestPasswordResetCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/RequestPasswordResetCommandHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAK9E,qBAAa,kCAAkC;IAGjC,OAAO,CAAC,QAAQ;IAF5B,OAAO,CAAC,YAAY,CAAe;gBAEf,QAAQ,EAAE,eAAe;IAIvC,OAAO,CAAC,GAAG,EAAE,2BAA2B,GAAG,OAAO,CAAC,OAAO,CAAC;CAsDlE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.js new file mode 100644 index 00000000..26090cf3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RequestPasswordResetCommandHandler = void 0; +const EmailService_1 = require("../../Services/EmailService"); +const TokenService_1 = require("../../Services/TokenService"); +const Logger_1 = require("../../Services/Logger"); +class RequestPasswordResetCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + this.emailService = new EmailService_1.EmailService(); + } + async execute(cmd) { + try { + if (!cmd.email) { + throw new Error('Email is required'); + } + // Find user by email + const user = await this.userRepo.findByEmail(cmd.email); + if (!user) { + // Don't reveal if user exists or not for security reasons + // Still return true but don't send email + (0, Logger_1.logAuth)(`Password reset requested for non-existent email: ${cmd.email}`); + return true; + } + // Generate password reset token + const resetTokenData = TokenService_1.TokenService.generatePasswordResetToken(); + // Update user with reset token + user.token = await TokenService_1.TokenService.hashToken(resetTokenData.token); + user.TokenExpires = resetTokenData.expiresAt; + await this.userRepo.update(user.id, user); + // Send password reset email + try { + const baseUrl = process.env.APP_BASE_URL || 'http://localhost:3000'; + const resetUrl = TokenService_1.TokenService.generatePasswordResetUrl(baseUrl, resetTokenData.token); + const emailSent = await this.emailService.sendPasswordResetEmail(user.email, `${user.fname} ${user.lname}`, resetTokenData.token, resetUrl); + if (!emailSent) { + (0, Logger_1.logWarning)(`Failed to send password reset email to ${user.email}`); + // Don't throw error - request should still succeed even if email fails + } + else { + (0, Logger_1.logAuth)(`Password reset email sent successfully to ${user.email}`); + } + } + catch (emailError) { + (0, Logger_1.logError)('Error sending password reset email', emailError instanceof Error ? emailError : new Error(String(emailError))); + // Don't throw error - request should still succeed even if email fails + } + return true; + } + catch (error) { + (0, Logger_1.logError)('Password reset request error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } +} +exports.RequestPasswordResetCommandHandler = RequestPasswordResetCommandHandler; +//# sourceMappingURL=RequestPasswordResetCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.js.map new file mode 100644 index 00000000..53d6e839 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/RequestPasswordResetCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestPasswordResetCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/RequestPasswordResetCommandHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAC3D,8DAA2D;AAC3D,kDAAsE;AAEtE,MAAa,kCAAkC;IAG7C,YAAoB,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;QAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,2BAAY,EAAE,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAgC;QAC5C,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;YAED,qBAAqB;YACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAExD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,0DAA0D;gBAC1D,yCAAyC;gBACzC,IAAA,gBAAO,EAAC,oDAAoD,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;gBACzE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,gCAAgC;YAChC,MAAM,cAAc,GAAG,2BAAY,CAAC,0BAA0B,EAAE,CAAC;YAEjE,+BAA+B;YAC/B,IAAI,CAAC,KAAK,GAAG,MAAM,2BAAY,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YAChE,IAAI,CAAC,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC;YAE7C,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAE1C,4BAA4B;YAC5B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB,CAAC;gBACpE,MAAM,QAAQ,GAAG,2BAAY,CAAC,wBAAwB,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;gBAEtF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,sBAAsB,CAC9D,IAAI,CAAC,KAAK,EACV,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,EAC7B,cAAc,CAAC,KAAK,EACpB,QAAQ,CACT,CAAC;gBAEF,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,IAAA,mBAAU,EAAC,0CAA0C,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;oBACnE,uEAAuE;gBACzE,CAAC;qBAAM,CAAC;oBACN,IAAA,gBAAO,EAAC,6CAA6C,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;YAAC,OAAO,UAAU,EAAE,CAAC;gBACpB,IAAA,iBAAQ,EAAC,oCAAoC,EAAE,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACzH,uEAAuE;YACzE,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,8BAA8B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACpG,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AA7DD,gFA6DC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.d.ts new file mode 100644 index 00000000..9c656982 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.d.ts @@ -0,0 +1,5 @@ +export interface ResetPasswordCommand { + token: string; + newPassword: string; +} +//# sourceMappingURL=ResetPasswordCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.d.ts.map new file mode 100644 index 00000000..e2bc2df2 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ResetPasswordCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/ResetPasswordCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.js b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.js new file mode 100644 index 00000000..cbe6b3e7 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=ResetPasswordCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.js.map new file mode 100644 index 00000000..e796182e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ResetPasswordCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/ResetPasswordCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.d.ts new file mode 100644 index 00000000..ab485d00 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { ResetPasswordCommand } from './ResetPasswordCommand'; +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +export declare class ResetPasswordCommandHandler { + private userRepo; + constructor(userRepo: IUserRepository); + execute(cmd: ResetPasswordCommand): Promise; +} +//# sourceMappingURL=ResetPasswordCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.d.ts.map new file mode 100644 index 00000000..aacae54b --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ResetPasswordCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/ResetPasswordCommandHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAK9E,qBAAa,2BAA2B;IAC1B,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEvC,OAAO,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;CA+C3D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.js new file mode 100644 index 00000000..c414e88d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ResetPasswordCommandHandler = void 0; +const TokenService_1 = require("../../Services/TokenService"); +const PasswordService_1 = require("../../Services/PasswordService"); +const Logger_1 = require("../../Services/Logger"); +class ResetPasswordCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(cmd) { + try { + if (!cmd.token) { + throw new Error('Reset token is required'); + } + if (!cmd.newPassword) { + throw new Error('New password is required'); + } + // Validate password strength + const validation = PasswordService_1.PasswordService.validatePasswordStrength(cmd.newPassword); + if (!validation.isValid) { + throw new Error(`Password validation failed: ${validation.errors.join(', ')}`); + } + // Hash the token to compare with stored value + const hashedToken = await TokenService_1.TokenService.hashToken(cmd.token); + // Find user with this password reset token + const user = await this.userRepo.findByToken(hashedToken); + if (!user) { + throw new Error('Invalid or expired reset token'); + } + // Check if token is expired + if (user.TokenExpires && user.TokenExpires < new Date()) { + throw new Error('Reset token has expired'); + } + // Hash the new password + const hashedPassword = await PasswordService_1.PasswordService.hashPassword(cmd.newPassword); + // Update user password and clear reset token + user.password = hashedPassword; + user.token = null; + user.TokenExpires = null; + await this.userRepo.update(user.id, user); + return true; + } + catch (error) { + (0, Logger_1.logError)('Password reset error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } +} +exports.ResetPasswordCommandHandler = ResetPasswordCommandHandler; +//# sourceMappingURL=ResetPasswordCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.js.map new file mode 100644 index 00000000..8695d83d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/ResetPasswordCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ResetPasswordCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/ResetPasswordCommandHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAC3D,oEAAiE;AACjE,kDAAiD;AAEjD,MAAa,2BAA2B;IACtC,YAAoB,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAEjD,KAAK,CAAC,OAAO,CAAC,GAAyB;QACrC,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC7C,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAC9C,CAAC;YAED,6BAA6B;YAC7B,MAAM,UAAU,GAAG,iCAAe,CAAC,wBAAwB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7E,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,+BAA+B,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjF,CAAC;YAED,8CAA8C;YAC9C,MAAM,WAAW,GAAG,MAAM,2BAAY,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAE5D,2CAA2C;YAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAE1D,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACpD,CAAC;YAED,4BAA4B;YAC5B,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;gBACxD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC7C,CAAC;YAED,wBAAwB;YACxB,MAAM,cAAc,GAAG,MAAM,iCAAe,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAE3E,6CAA6C;YAC7C,IAAI,CAAC,QAAQ,GAAG,cAAc,CAAC;YAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAEzB,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAE1C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,sBAAsB,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5F,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAlDD,kEAkDC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.d.ts new file mode 100644 index 00000000..36afce54 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.d.ts @@ -0,0 +1,14 @@ +export interface UpdateUserCommand { + id: string; + orgid?: string; + username?: string; + password?: string; + email?: string; + fname?: string; + lname?: string; + code?: string; + type?: string; + phone?: string; + state?: number; +} +//# sourceMappingURL=UpdateUserCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.d.ts.map new file mode 100644 index 00000000..9b96563f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateUserCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/UpdateUserCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.js b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.js new file mode 100644 index 00000000..01fb2e59 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=UpdateUserCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.js.map new file mode 100644 index 00000000..526ebf6e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateUserCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/UpdateUserCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.d.ts new file mode 100644 index 00000000..a8becc6f --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.d.ts @@ -0,0 +1,9 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { UpdateUserCommand } from './UpdateUserCommand'; +import { ShortUserDto } from '../../DTOs/UserDto'; +export declare class UpdateUserCommandHandler { + private readonly userRepo; + constructor(userRepo: IUserRepository); + execute(cmd: UpdateUserCommand): Promise; +} +//# sourceMappingURL=UpdateUserCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.d.ts.map new file mode 100644 index 00000000..68a7b0f5 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateUserCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/UpdateUserCommandHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAkBpE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.js new file mode 100644 index 00000000..48e89a36 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateUserCommandHandler = void 0; +const UserMapper_1 = require("../../DTOs/Mappers/UserMapper"); +const PasswordService_1 = require("../../Services/PasswordService"); +class UpdateUserCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(cmd) { + const updateData = { ...cmd }; + // Hash the password if it's being updated + if (cmd.password) { + // Validate password strength + const passwordValidation = PasswordService_1.PasswordService.validatePasswordStrength(cmd.password); + if (!passwordValidation.isValid) { + throw new Error(`Password validation failed: ${passwordValidation.errors.join(', ')}`); + } + updateData.password = await PasswordService_1.PasswordService.hashPassword(cmd.password); + } + const updated = await this.userRepo.update(cmd.id, updateData); + if (!updated) + return null; + return UserMapper_1.UserMapper.toShortDto(updated); + } +} +exports.UpdateUserCommandHandler = UpdateUserCommandHandler; +//# sourceMappingURL=UpdateUserCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.js.map new file mode 100644 index 00000000..908e6f9e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/UpdateUserCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UpdateUserCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/UpdateUserCommandHandler.ts"],"names":[],"mappings":";;;AAIA,8DAA2D;AAC3D,oEAAiE;AAEjE,MAAa,wBAAwB;IACnC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,GAAsB;QAClC,MAAM,UAAU,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;QAE9B,0CAA0C;QAC1C,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACjB,6BAA6B;YAC7B,MAAM,kBAAkB,GAAG,iCAAe,CAAC,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClF,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,+BAA+B,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzF,CAAC;YAED,UAAU,CAAC,QAAQ,GAAG,MAAM,iCAAe,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,OAAO,uBAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;CACF;AArBD,4DAqBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.d.ts b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.d.ts new file mode 100644 index 00000000..cf28fa54 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.d.ts @@ -0,0 +1,4 @@ +export interface VerifyEmailCommand { + token: string; +} +//# sourceMappingURL=VerifyEmailCommand.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.d.ts.map new file mode 100644 index 00000000..d4bf431d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"VerifyEmailCommand.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/VerifyEmailCommand.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;CACf"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.js b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.js new file mode 100644 index 00000000..cfd1fe4d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=VerifyEmailCommand.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.js.map b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.js.map new file mode 100644 index 00000000..13e82a02 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommand.js.map @@ -0,0 +1 @@ +{"version":3,"file":"VerifyEmailCommand.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/VerifyEmailCommand.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.d.ts b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.d.ts new file mode 100644 index 00000000..2cf6bce7 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.d.ts @@ -0,0 +1,8 @@ +import { VerifyEmailCommand } from './VerifyEmailCommand'; +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +export declare class VerifyEmailCommandHandler { + private userRepo; + constructor(userRepo: IUserRepository); + execute(cmd: VerifyEmailCommand): Promise; +} +//# sourceMappingURL=VerifyEmailCommandHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.d.ts.map new file mode 100644 index 00000000..0d222913 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"VerifyEmailCommandHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/commands/VerifyEmailCommandHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAK9E,qBAAa,yBAAyB;IACxB,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEvC,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;CAkCzD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.js b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.js new file mode 100644 index 00000000..517df5a3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.js @@ -0,0 +1,41 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VerifyEmailCommandHandler = void 0; +const TokenService_1 = require("../../Services/TokenService"); +const UserAggregate_1 = require("../../../Domain/User/UserAggregate"); +const Logger_1 = require("../../Services/Logger"); +class VerifyEmailCommandHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(cmd) { + try { + if (!cmd.token) { + throw new Error('Verification token is required'); + } + // Hash the token to compare with stored value + const hashedToken = await TokenService_1.TokenService.hashToken(cmd.token); + // Find user with this verification token + const user = await this.userRepo.findByToken(hashedToken); + if (!user) { + throw new Error('Invalid or expired verification token'); + } + // Check if token is expired + if (user.TokenExpires && user.TokenExpires < new Date()) { + throw new Error('Verification token has expired'); + } + // Update user verification status + user.token = null; + user.TokenExpires = null; + user.state = UserAggregate_1.UserState.VERIFIED_REGULAR; + await this.userRepo.update(user.id, user); + return true; + } + catch (error) { + (0, Logger_1.logError)('Email verification error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } +} +exports.VerifyEmailCommandHandler = VerifyEmailCommandHandler; +//# sourceMappingURL=VerifyEmailCommandHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.js.map b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.js.map new file mode 100644 index 00000000..9c707912 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/commands/VerifyEmailCommandHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"VerifyEmailCommandHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/commands/VerifyEmailCommandHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAC3D,sEAA+D;AAC/D,kDAAiD;AAEjD,MAAa,yBAAyB;IACpC,YAAoB,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAEjD,KAAK,CAAC,OAAO,CAAC,GAAuB;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACpD,CAAC;YAED,8CAA8C;YAC9C,MAAM,WAAW,GAAG,MAAM,2BAAY,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAE5D,yCAAyC;YACzC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAE1D,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;YAC3D,CAAC;YAED,4BAA4B;YAC5B,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;gBACxD,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACpD,CAAC;YAED,kCAAkC;YAClC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,yBAAS,CAAC,gBAAgB,CAAC;YAExC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAE1C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,0BAA0B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAChG,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AArCD,8DAqCC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.d.ts b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.d.ts new file mode 100644 index 00000000..5fe3756d --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.d.ts @@ -0,0 +1,4 @@ +export interface GetUserByIdQuery { + id: string; +} +//# sourceMappingURL=GetUserByIdQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.d.ts.map b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.d.ts.map new file mode 100644 index 00000000..a72115c9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUserByIdQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUserByIdQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;CACZ"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.js b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.js new file mode 100644 index 00000000..44a291f9 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetUserByIdQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.js.map b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.js.map new file mode 100644 index 00000000..8fc43506 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUserByIdQuery.js","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUserByIdQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.d.ts new file mode 100644 index 00000000..46254eed --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.d.ts @@ -0,0 +1,9 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { GetUserByIdQuery } from './GetUserByIdQuery'; +import { ShortUserDto } from '../../DTOs/UserDto'; +export declare class GetUserByIdQueryHandler { + private readonly userRepo; + constructor(userRepo: IUserRepository); + execute(query: GetUserByIdQuery): Promise; +} +//# sourceMappingURL=GetUserByIdQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.d.ts.map new file mode 100644 index 00000000..53a9a43e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUserByIdQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUserByIdQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAsBrE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.js b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.js new file mode 100644 index 00000000..d2a157c3 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetUserByIdQueryHandler = void 0; +const UserMapper_1 = require("../../DTOs/Mappers/UserMapper"); +const Logger_1 = require("../../Services/Logger"); +class GetUserByIdQueryHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(query) { + try { + const user = await this.userRepo.findById(query.id); + if (!user) + return null; + return UserMapper_1.UserMapper.toShortDto(user); + } + catch (error) { + (0, Logger_1.logError)('GetUserByIdQueryHandler error', error instanceof Error ? error : new Error(String(error))); + // Handle invalid ID format + if (error instanceof Error && error.message.includes('invalid') && error.message.includes('uuid')) { + return null; // Treat invalid UUID as not found + } + // Handle database errors + if (error instanceof Error && error.message.includes('database')) { + throw new Error('Database connection error'); + } + // Generic error for other cases + throw new Error('Failed to retrieve user'); + } + } +} +exports.GetUserByIdQueryHandler = GetUserByIdQueryHandler; +//# sourceMappingURL=GetUserByIdQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.js.map b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.js.map new file mode 100644 index 00000000..56245d68 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUserByIdQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUserByIdQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUserByIdQueryHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAC3D,kDAAiD;AAEjD,MAAa,uBAAuB;IAClC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,KAAuB;QACnC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACpD,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO,uBAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,+BAA+B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAErG,2BAA2B;YAC3B,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClG,OAAO,IAAI,CAAC,CAAC,kCAAkC;YACjD,CAAC;YAED,yBAAyB;YACzB,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC/C,CAAC;YAED,gCAAgC;YAChC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;CACF;AAzBD,0DAyBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.d.ts b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.d.ts new file mode 100644 index 00000000..a570b1e4 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.d.ts @@ -0,0 +1,6 @@ +export interface GetUsersByPageQuery { + from: number; + to: number; + includeDeleted?: boolean; +} +//# sourceMappingURL=GetUsersByPageQuery.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.d.ts.map b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.d.ts.map new file mode 100644 index 00000000..bf447d19 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUsersByPageQuery.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUsersByPageQuery.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.js b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.js new file mode 100644 index 00000000..8672699e --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=GetUsersByPageQuery.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.js.map b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.js.map new file mode 100644 index 00000000..2989ee22 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQuery.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUsersByPageQuery.js","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUsersByPageQuery.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.d.ts b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.d.ts new file mode 100644 index 00000000..f8c71d49 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.d.ts @@ -0,0 +1,12 @@ +import { IUserRepository } from '../../../Domain/IRepository/IUserRepository'; +import { GetUsersByPageQuery } from './GetUsersByPageQuery'; +import { ShortUserDto } from '../../DTOs/UserDto'; +export declare class GetUsersByPageQueryHandler { + private readonly userRepo; + constructor(userRepo: IUserRepository); + execute(query: GetUsersByPageQuery): Promise<{ + users: ShortUserDto[]; + totalCount: number; + }>; +} +//# sourceMappingURL=GetUsersByPageQueryHandler.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.d.ts.map b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.d.ts.map new file mode 100644 index 00000000..1ae4b293 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUsersByPageQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUsersByPageQueryHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAkDlG"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.js b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.js new file mode 100644 index 00000000..9a6aafb6 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.js @@ -0,0 +1,55 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetUsersByPageQueryHandler = void 0; +const UserMapper_1 = require("../../DTOs/Mappers/UserMapper"); +const Logger_1 = require("../../Services/Logger"); +class GetUsersByPageQueryHandler { + constructor(userRepo) { + this.userRepo = userRepo; + } + async execute(query) { + try { + // Validate pagination parameters + if (query.from < 0 || query.to < query.from) { + throw new Error('Invalid pagination parameters'); + } + const limit = query.to - query.from + 1; + if (limit > 100) { + throw new Error('Page size too large. Maximum 100 records per request'); + } + (0, Logger_1.logRequest)('Get users by page query started', undefined, undefined, { + from: query.from, + to: query.to, + includeDeleted: query.includeDeleted || false + }); + const result = query.includeDeleted + ? await this.userRepo.findByPageIncludingDeleted(query.from, query.to) + : await this.userRepo.findByPage(query.from, query.to); + (0, Logger_1.logRequest)('Get users by page query completed', undefined, undefined, { + from: query.from, + to: query.to, + returned: result.users.length, + totalCount: result.totalCount, + includeDeleted: query.includeDeleted || false + }); + return { + users: UserMapper_1.UserMapper.toShortDtoList(result.users), + totalCount: result.totalCount + }; + } + catch (error) { + (0, Logger_1.logError)('GetUsersByPageQueryHandler error', error instanceof Error ? error : new Error(String(error))); + // Handle database errors + if (error instanceof Error && error.message.includes('database')) { + throw new Error('Database connection error'); + } + // Re-throw validation errors as-is + if (error instanceof Error && (error.message.includes('Invalid pagination') || error.message.includes('Page size'))) { + throw error; + } + throw new Error('Failed to retrieve users'); + } + } +} +exports.GetUsersByPageQueryHandler = GetUsersByPageQueryHandler; +//# sourceMappingURL=GetUsersByPageQueryHandler.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.js.map b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.js.map new file mode 100644 index 00000000..035a1b02 --- /dev/null +++ b/SerpentRace_Backend/dist/Application/User/queries/GetUsersByPageQueryHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GetUsersByPageQueryHandler.js","sourceRoot":"","sources":["../../../../src/Application/User/queries/GetUsersByPageQueryHandler.ts"],"names":[],"mappings":";;;AAGA,8DAA2D;AAC3D,kDAA6D;AAE7D,MAAa,0BAA0B;IACrC,YAA6B,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAE1D,KAAK,CAAC,OAAO,CAAC,KAA0B;QACtC,IAAI,CAAC;YACH,iCAAiC;YACjC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;YACxC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;YAC1E,CAAC;YAED,IAAA,mBAAU,EAAC,iCAAiC,EAAE,SAAS,EAAE,SAAS,EAAE;gBAClE,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;aAC9C,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc;gBACjC,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACtE,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAEzD,IAAA,mBAAU,EAAC,mCAAmC,EAAE,SAAS,EAAE,SAAS,EAAE;gBACpE,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;gBAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;aAC9C,CAAC,CAAC;YAEH,OAAO;gBACL,KAAK,EAAE,uBAAU,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC9C,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAA,iBAAQ,EAAC,kCAAkC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAExG,yBAAyB;YACzB,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC/C,CAAC;YAED,mCAAmC;YACnC,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBACpH,MAAM,KAAK,CAAC;YACd,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;CACF;AArDD,gEAqDC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.d.ts b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.d.ts new file mode 100644 index 00000000..a9f8d58e --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.d.ts @@ -0,0 +1,33 @@ +export interface Message { + id: string; + date: Date; + userid: string; + text: string; +} +export declare const ChatState: { + readonly ACTIVE: 0; + readonly ARCHIVE: 1; + readonly SOFT_DELETE: 2; +}; +export type ChatStateType = typeof ChatState[keyof typeof ChatState]; +export declare const ChatType: { + readonly DIRECT: "direct"; + readonly GROUP: "group"; + readonly GAME: "game"; +}; +export type ChatTypeType = typeof ChatType[keyof typeof ChatType]; +export declare class ChatAggregate { + id: string; + type: ChatTypeType; + name: string | null; + gameId: string | null; + createdBy: string | null; + users: string[]; + messages: Message[]; + lastActivity: Date | null; + createDate: Date; + updateDate: Date; + state: ChatStateType; + archiveDate: Date | null; +} +//# sourceMappingURL=ChatAggregate.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.d.ts.map b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.d.ts.map new file mode 100644 index 00000000..1ca0f86e --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatAggregate.d.ts","sourceRoot":"","sources":["../../../src/Domain/Chat/ChatAggregate.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,OAAO,SAAS,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAErE,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,OAAO,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAElE,qBACa,aAAa;IAEtB,EAAE,EAAG,MAAM,CAAC;IAGZ,IAAI,EAAG,YAAY,CAAC;IAGpB,IAAI,EAAG,MAAM,GAAG,IAAI,CAAC;IAGrB,MAAM,EAAG,MAAM,GAAG,IAAI,CAAC;IAGvB,SAAS,EAAG,MAAM,GAAG,IAAI,CAAC;IAG1B,KAAK,EAAG,MAAM,EAAE,CAAC;IAGjB,QAAQ,EAAG,OAAO,EAAE,CAAC;IAGrB,YAAY,EAAG,IAAI,GAAG,IAAI,CAAC;IAG3B,UAAU,EAAG,IAAI,CAAC;IAGlB,UAAU,EAAG,IAAI,CAAC;IAGlB,KAAK,EAAG,aAAa,CAAC;IAItB,WAAW,EAAG,IAAI,GAAG,IAAI,CAAC;CAC7B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.js b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.js new file mode 100644 index 00000000..1cf28598 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.js @@ -0,0 +1,78 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatAggregate = exports.ChatType = exports.ChatState = void 0; +const typeorm_1 = require("typeorm"); +exports.ChatState = { + ACTIVE: 0, + ARCHIVE: 1, + SOFT_DELETE: 2 +}; +exports.ChatType = { + DIRECT: 'direct', + GROUP: 'group', + GAME: 'game' +}; +let ChatAggregate = class ChatAggregate { +}; +exports.ChatAggregate = ChatAggregate; +__decorate([ + (0, typeorm_1.PrimaryGeneratedColumn)('uuid'), + __metadata("design:type", String) +], ChatAggregate.prototype, "id", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 50, default: exports.ChatType.DIRECT }), + __metadata("design:type", String) +], ChatAggregate.prototype, "type", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }), + __metadata("design:type", Object) +], ChatAggregate.prototype, "name", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', nullable: true }), + __metadata("design:type", Object) +], ChatAggregate.prototype, "gameId", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', nullable: true }), + __metadata("design:type", Object) +], ChatAggregate.prototype, "createdBy", void 0); +__decorate([ + (0, typeorm_1.Column)('uuid', { array: true }), + __metadata("design:type", Array) +], ChatAggregate.prototype, "users", void 0); +__decorate([ + (0, typeorm_1.Column)('json', { default: [] }), + __metadata("design:type", Array) +], ChatAggregate.prototype, "messages", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'timestamp', nullable: true }), + __metadata("design:type", Object) +], ChatAggregate.prototype, "lastActivity", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)(), + __metadata("design:type", Date) +], ChatAggregate.prototype, "createDate", void 0); +__decorate([ + (0, typeorm_1.UpdateDateColumn)(), + __metadata("design:type", Date) +], ChatAggregate.prototype, "updateDate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: exports.ChatState.ACTIVE }), + __metadata("design:type", Number) +], ChatAggregate.prototype, "state", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'timestamp', nullable: true }), + __metadata("design:type", Object) +], ChatAggregate.prototype, "archiveDate", void 0); +exports.ChatAggregate = ChatAggregate = __decorate([ + (0, typeorm_1.Entity)('Chats') +], ChatAggregate); +//# sourceMappingURL=ChatAggregate.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.js.map b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.js.map new file mode 100644 index 00000000..68ebc4be --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatAggregate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatAggregate.js","sourceRoot":"","sources":["../../../src/Domain/Chat/ChatAggregate.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAqG;AASxF,QAAA,SAAS,GAAG;IACrB,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,CAAC;CACR,CAAC;AAIE,QAAA,QAAQ,GAAG;IACpB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;CACN,CAAC;AAKJ,IAAM,aAAa,GAAnB,MAAM,aAAa;CAqCzB,CAAA;AArCY,sCAAa;AAEtB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;yCACnB;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,gBAAQ,CAAC,MAAM,EAAE,CAAC;;2CAC9C;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACpC;AAGrB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CAClB;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACf;AAG1B;IADC,IAAA,gBAAM,EAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;;4CACf;AAGjB;IADC,IAAA,gBAAM,EAAC,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;;+CACX;AAGrB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACnB;AAG3B;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;iDAAC;AAGlB;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;iDAAC;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAS,CAAC,MAAM,EAAE,CAAC;;4CAC7B;AAItB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACpB;wBApCjB,aAAa;IADzB,IAAA,gBAAM,EAAC,OAAO,CAAC;GACH,aAAa,CAqCzB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.d.ts b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.d.ts new file mode 100644 index 00000000..323fe672 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.d.ts @@ -0,0 +1,13 @@ +import { Message } from './ChatAggregate'; +export declare class ChatArchiveAggregate { + id: string; + chatId: string; + archivedMessages: Message[]; + archivedAt: Date; + createDate: Date; + chatType: string; + chatName: string | null; + gameId: string | null; + participants: string[]; +} +//# sourceMappingURL=ChatArchiveAggregate.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.d.ts.map b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.d.ts.map new file mode 100644 index 00000000..50c3fdf1 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatArchiveAggregate.d.ts","sourceRoot":"","sources":["../../../src/Domain/Chat/ChatArchiveAggregate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,qBACa,oBAAoB;IAE7B,EAAE,EAAG,MAAM,CAAC;IAGZ,MAAM,EAAG,MAAM,CAAC;IAGhB,gBAAgB,EAAG,OAAO,EAAE,CAAC;IAG7B,UAAU,EAAG,IAAI,CAAC;IAGlB,UAAU,EAAG,IAAI,CAAC;IAIlB,QAAQ,EAAG,MAAM,CAAC;IAGlB,QAAQ,EAAG,MAAM,GAAG,IAAI,CAAC;IAGzB,MAAM,EAAG,MAAM,GAAG,IAAI,CAAC;IAGvB,YAAY,EAAG,MAAM,EAAE,CAAC;CAC3B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.js b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.js new file mode 100644 index 00000000..b18db602 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.js @@ -0,0 +1,56 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatArchiveAggregate = void 0; +const typeorm_1 = require("typeorm"); +let ChatArchiveAggregate = class ChatArchiveAggregate { +}; +exports.ChatArchiveAggregate = ChatArchiveAggregate; +__decorate([ + (0, typeorm_1.PrimaryGeneratedColumn)('uuid'), + __metadata("design:type", String) +], ChatArchiveAggregate.prototype, "id", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid' }), + __metadata("design:type", String) +], ChatArchiveAggregate.prototype, "chatId", void 0); +__decorate([ + (0, typeorm_1.Column)('json'), + __metadata("design:type", Array) +], ChatArchiveAggregate.prototype, "archivedMessages", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'timestamp' }), + __metadata("design:type", Date) +], ChatArchiveAggregate.prototype, "archivedAt", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)(), + __metadata("design:type", Date) +], ChatArchiveAggregate.prototype, "createDate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 50 }), + __metadata("design:type", String) +], ChatArchiveAggregate.prototype, "chatType", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }), + __metadata("design:type", Object) +], ChatArchiveAggregate.prototype, "chatName", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', nullable: true }), + __metadata("design:type", Object) +], ChatArchiveAggregate.prototype, "gameId", void 0); +__decorate([ + (0, typeorm_1.Column)('uuid', { array: true }), + __metadata("design:type", Array) +], ChatArchiveAggregate.prototype, "participants", void 0); +exports.ChatArchiveAggregate = ChatArchiveAggregate = __decorate([ + (0, typeorm_1.Entity)('ChatArchives') +], ChatArchiveAggregate); +//# sourceMappingURL=ChatArchiveAggregate.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.js.map b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.js.map new file mode 100644 index 00000000..c2743308 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Chat/ChatArchiveAggregate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatArchiveAggregate.js","sourceRoot":"","sources":["../../../src/Domain/Chat/ChatArchiveAggregate.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAmF;AAI5E,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;CA4BhC,CAAA;AA5BY,oDAAoB;AAE7B;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;gDACnB;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDACT;AAGhB;IADC,IAAA,gBAAM,EAAC,MAAM,CAAC;;8DACc;AAG7B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;8BACjB,IAAI;wDAAC;AAGlB;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;wDAAC;AAIlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;sDACtB;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sDAChC;AAGzB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDAClB;AAGvB;IADC,IAAA,gBAAM,EAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;;0DACR;+BA3Bf,oBAAoB;IADhC,IAAA,gBAAM,EAAC,cAAc,CAAC;GACV,oBAAoB,CA4BhC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.d.ts b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.d.ts new file mode 100644 index 00000000..dd91d566 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.d.ts @@ -0,0 +1,27 @@ +export declare enum ContactType { + BUG = 0, + PROBLEM = 1, + QUESTION = 2, + SALES = 3, + OTHER = 4 +} +export declare enum ContactState { + ACTIVE = 0, + RESOLVED = 1, + SOFT_DELETE = 2 +} +export declare class ContactAggregate { + id: string; + name: string; + email: string; + userid: string | null; + type: ContactType; + txt: string; + state: ContactState; + createDate: Date; + updateDate: Date; + adminResponse: string | null; + responseDate: Date | null; + respondedBy: string | null; +} +//# sourceMappingURL=ContactAggregate.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.d.ts.map b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.d.ts.map new file mode 100644 index 00000000..5babc11b --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactAggregate.d.ts","sourceRoot":"","sources":["../../../src/Domain/Contact/ContactAggregate.ts"],"names":[],"mappings":"AAEA,oBAAY,WAAW;IACnB,GAAG,IAAI;IACP,OAAO,IAAI;IACX,QAAQ,IAAI;IACZ,KAAK,IAAI;IACT,KAAK,IAAI;CACZ;AAED,oBAAY,YAAY;IACpB,MAAM,IAAI;IACV,QAAQ,IAAI;IACZ,WAAW,IAAI;CAClB;AAED,qBACa,gBAAgB;IAEzB,EAAE,EAAG,MAAM,CAAC;IAGZ,IAAI,EAAG,MAAM,CAAC;IAGd,KAAK,EAAG,MAAM,CAAC;IAGf,MAAM,EAAG,MAAM,GAAG,IAAI,CAAC;IAGvB,IAAI,EAAG,WAAW,CAAC;IAGnB,GAAG,EAAG,MAAM,CAAC;IAGb,KAAK,EAAG,YAAY,CAAC;IAGrB,UAAU,EAAG,IAAI,CAAC;IAGlB,UAAU,EAAG,IAAI,CAAC;IAIlB,aAAa,EAAG,MAAM,GAAG,IAAI,CAAC;IAG9B,YAAY,EAAG,IAAI,GAAG,IAAI,CAAC;IAG3B,WAAW,EAAG,MAAM,GAAG,IAAI,CAAC;CAC/B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.js b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.js new file mode 100644 index 00000000..6accea8d --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.js @@ -0,0 +1,82 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContactAggregate = exports.ContactState = exports.ContactType = void 0; +const typeorm_1 = require("typeorm"); +var ContactType; +(function (ContactType) { + ContactType[ContactType["BUG"] = 0] = "BUG"; + ContactType[ContactType["PROBLEM"] = 1] = "PROBLEM"; + ContactType[ContactType["QUESTION"] = 2] = "QUESTION"; + ContactType[ContactType["SALES"] = 3] = "SALES"; + ContactType[ContactType["OTHER"] = 4] = "OTHER"; +})(ContactType || (exports.ContactType = ContactType = {})); +var ContactState; +(function (ContactState) { + ContactState[ContactState["ACTIVE"] = 0] = "ACTIVE"; + ContactState[ContactState["RESOLVED"] = 1] = "RESOLVED"; + ContactState[ContactState["SOFT_DELETE"] = 2] = "SOFT_DELETE"; +})(ContactState || (exports.ContactState = ContactState = {})); +let ContactAggregate = class ContactAggregate { +}; +exports.ContactAggregate = ContactAggregate; +__decorate([ + (0, typeorm_1.PrimaryGeneratedColumn)('uuid'), + __metadata("design:type", String) +], ContactAggregate.prototype, "id", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255 }), + __metadata("design:type", String) +], ContactAggregate.prototype, "name", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255 }), + __metadata("design:type", String) +], ContactAggregate.prototype, "email", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', nullable: true }), + __metadata("design:type", Object) +], ContactAggregate.prototype, "userid", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int' }), + __metadata("design:type", Number) +], ContactAggregate.prototype, "type", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'text' }), + __metadata("design:type", String) +], ContactAggregate.prototype, "txt", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: ContactState.ACTIVE }), + __metadata("design:type", Number) +], ContactAggregate.prototype, "state", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)(), + __metadata("design:type", Date) +], ContactAggregate.prototype, "createDate", void 0); +__decorate([ + (0, typeorm_1.UpdateDateColumn)(), + __metadata("design:type", Date) +], ContactAggregate.prototype, "updateDate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'text', nullable: true }), + __metadata("design:type", Object) +], ContactAggregate.prototype, "adminResponse", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'timestamp', nullable: true }), + __metadata("design:type", Object) +], ContactAggregate.prototype, "responseDate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', nullable: true }), + __metadata("design:type", Object) +], ContactAggregate.prototype, "respondedBy", void 0); +exports.ContactAggregate = ContactAggregate = __decorate([ + (0, typeorm_1.Entity)('Contacts') +], ContactAggregate); +//# sourceMappingURL=ContactAggregate.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.js.map b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.js.map new file mode 100644 index 00000000..31940035 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Contact/ContactAggregate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactAggregate.js","sourceRoot":"","sources":["../../../src/Domain/Contact/ContactAggregate.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAqG;AAErG,IAAY,WAMX;AAND,WAAY,WAAW;IACnB,2CAAO,CAAA;IACP,mDAAW,CAAA;IACX,qDAAY,CAAA;IACZ,+CAAS,CAAA;IACT,+CAAS,CAAA;AACb,CAAC,EANW,WAAW,2BAAX,WAAW,QAMtB;AAED,IAAY,YAIX;AAJD,WAAY,YAAY;IACpB,mDAAU,CAAA;IACV,uDAAY,CAAA;IACZ,6DAAe,CAAA;AACnB,CAAC,EAJW,YAAY,4BAAZ,YAAY,QAIvB;AAGM,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAqC5B,CAAA;AArCY,4CAAgB;AAEzB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;4CACnB;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;8CAC3B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;+CAC1B;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDAClB;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;8CACL;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;6CACZ;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC;;+CACjC;AAGrB;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;oDAAC;AAGlB;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;oDAAC;AAIlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uDACX;AAG9B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sDACnB;AAG3B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACb;2BApCnB,gBAAgB;IAD5B,IAAA,gBAAM,EAAC,UAAU,CAAC;GACN,gBAAgB,CAqC5B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.d.ts b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.d.ts new file mode 100644 index 00000000..89fba18d --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.d.ts @@ -0,0 +1,41 @@ +import { OrganizationAggregate } from '../Organization/OrganizationAggregate'; +export declare enum Type { + LUCK = 0, + JOKER = 1, + QUESTION = 2 +} +export declare enum CType { + PUBLIC = 0, + PRIVATE = 1, + ORGANIZATION = 2 +} +export declare enum State { + ACTIVE = 0, + SOFT_DELETE = 1 +} +export declare enum CardType { + QUIZ = 0, + SENTENCE_PAIRING = 1, + OWN_ANSWER = 2, + TRUE_FALSE = 3, + CLOSER = 4 +} +export interface Card { + text: string; + type?: CardType; + answer?: string | null; +} +export declare class DeckAggregate { + id: string; + name: string; + type: Type; + userid: string; + creationdate: Date; + cards: Card[]; + playedNumber: number; + ctype: CType; + updatedate: Date; + state: State; + organization: OrganizationAggregate | null; +} +//# sourceMappingURL=DeckAggregate.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.d.ts.map b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.d.ts.map new file mode 100644 index 00000000..a8624c32 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckAggregate.d.ts","sourceRoot":"","sources":["../../../src/Domain/Deck/DeckAggregate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,oBAAY,IAAI;IACZ,IAAI,IAAI;IACR,KAAK,IAAI;IACT,QAAQ,IAAI;CACf;AAED,oBAAY,KAAK;IACb,MAAM,IAAI;IACV,OAAO,IAAI;IACX,YAAY,IAAI;CACnB;AAED,oBAAY,KAAK;IACb,MAAM,IAAI;IACV,WAAW,IAAI;CAClB;AAED,oBAAY,QAAQ;IAChB,IAAI,IAAI;IACR,gBAAgB,IAAI;IACpB,UAAU,IAAI;IACd,UAAU,IAAI;IACd,MAAM,IAAI;CACb;AAED,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBACa,aAAa;IAEtB,EAAE,EAAG,MAAM,CAAC;IAGZ,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,IAAI,CAAC;IAGZ,MAAM,EAAG,MAAM,CAAC;IAGhB,YAAY,EAAG,IAAI,CAAC;IAGpB,KAAK,EAAG,IAAI,EAAE,CAAC;IAGf,YAAY,EAAG,MAAM,CAAC;IAGtB,KAAK,EAAG,KAAK,CAAC;IAGd,UAAU,EAAG,IAAI,CAAC;IAGd,KAAK,EAAG,KAAK,CAAC;IAIlB,YAAY,EAAG,qBAAqB,GAAG,IAAI,CAAC;CAC/C"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.js b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.js new file mode 100644 index 00000000..cbff1d3f --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.js @@ -0,0 +1,91 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeckAggregate = exports.CardType = exports.State = exports.CType = exports.Type = void 0; +const typeorm_1 = require("typeorm"); +const OrganizationAggregate_1 = require("../Organization/OrganizationAggregate"); +var Type; +(function (Type) { + Type[Type["LUCK"] = 0] = "LUCK"; + Type[Type["JOKER"] = 1] = "JOKER"; + Type[Type["QUESTION"] = 2] = "QUESTION"; +})(Type || (exports.Type = Type = {})); +var CType; +(function (CType) { + CType[CType["PUBLIC"] = 0] = "PUBLIC"; + CType[CType["PRIVATE"] = 1] = "PRIVATE"; + CType[CType["ORGANIZATION"] = 2] = "ORGANIZATION"; +})(CType || (exports.CType = CType = {})); +var State; +(function (State) { + State[State["ACTIVE"] = 0] = "ACTIVE"; + State[State["SOFT_DELETE"] = 1] = "SOFT_DELETE"; +})(State || (exports.State = State = {})); +var CardType; +(function (CardType) { + CardType[CardType["QUIZ"] = 0] = "QUIZ"; + CardType[CardType["SENTENCE_PAIRING"] = 1] = "SENTENCE_PAIRING"; + CardType[CardType["OWN_ANSWER"] = 2] = "OWN_ANSWER"; + CardType[CardType["TRUE_FALSE"] = 3] = "TRUE_FALSE"; + CardType[CardType["CLOSER"] = 4] = "CLOSER"; +})(CardType || (exports.CardType = CardType = {})); +let DeckAggregate = class DeckAggregate { +}; +exports.DeckAggregate = DeckAggregate; +__decorate([ + (0, typeorm_1.PrimaryGeneratedColumn)('uuid'), + __metadata("design:type", String) +], DeckAggregate.prototype, "id", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255 }), + __metadata("design:type", String) +], DeckAggregate.prototype, "name", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int' }), + __metadata("design:type", Number) +], DeckAggregate.prototype, "type", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', name: 'user_id' }), + __metadata("design:type", String) +], DeckAggregate.prototype, "userid", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)({ name: 'creation_date' }), + __metadata("design:type", Date) +], DeckAggregate.prototype, "creationdate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'json' }), + __metadata("design:type", Array) +], DeckAggregate.prototype, "cards", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: 0, name: 'played_number' }), + __metadata("design:type", Number) +], DeckAggregate.prototype, "playedNumber", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: CType.PUBLIC }), + __metadata("design:type", Number) +], DeckAggregate.prototype, "ctype", void 0); +__decorate([ + (0, typeorm_1.UpdateDateColumn)({ name: 'update_date' }), + __metadata("design:type", Date) +], DeckAggregate.prototype, "updatedate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: State.ACTIVE }), + __metadata("design:type", Number) +], DeckAggregate.prototype, "state", void 0); +__decorate([ + (0, typeorm_1.ManyToOne)(() => OrganizationAggregate_1.OrganizationAggregate, { nullable: true }), + (0, typeorm_1.JoinColumn)({ name: 'organization_id' }), + __metadata("design:type", Object) +], DeckAggregate.prototype, "organization", void 0); +exports.DeckAggregate = DeckAggregate = __decorate([ + (0, typeorm_1.Entity)('Decks') +], DeckAggregate); +//# sourceMappingURL=DeckAggregate.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.js.map b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.js.map new file mode 100644 index 00000000..2b724067 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Deck/DeckAggregate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckAggregate.js","sourceRoot":"","sources":["../../../src/Domain/Deck/DeckAggregate.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA4H;AAC5H,iFAA8E;AAE9E,IAAY,IAIX;AAJD,WAAY,IAAI;IACZ,+BAAQ,CAAA;IACR,iCAAS,CAAA;IACT,uCAAY,CAAA;AAChB,CAAC,EAJW,IAAI,oBAAJ,IAAI,QAIf;AAED,IAAY,KAIX;AAJD,WAAY,KAAK;IACb,qCAAU,CAAA;IACV,uCAAW,CAAA;IACX,iDAAgB,CAAA;AACpB,CAAC,EAJW,KAAK,qBAAL,KAAK,QAIhB;AAED,IAAY,KAGX;AAHD,WAAY,KAAK;IACb,qCAAU,CAAA;IACV,+CAAe,CAAA;AACnB,CAAC,EAHW,KAAK,qBAAL,KAAK,QAGhB;AAED,IAAY,QAMX;AAND,WAAY,QAAQ;IAChB,uCAAQ,CAAA;IACR,+DAAoB,CAAA;IACpB,mDAAc,CAAA;IACd,mDAAc,CAAA;IACd,2CAAU,CAAA;AACd,CAAC,EANW,QAAQ,wBAAR,QAAQ,QAMnB;AASM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAkCzB,CAAA;AAlCY,sCAAa;AAEtB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;yCACnB;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;2CAC3B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAC,CAAC;;2CACX;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;6CAC1B;AAGhB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;8BAC7B,IAAI;mDAAC;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;4CACV;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;;mDACrC;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;;4CACjC;AAGd;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;8BAC7B,IAAI;iDAAC;AAGd;IADH,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;;4CAC7B;AAIlB;IAFC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,6CAAqB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1D,IAAA,oBAAU,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;;mDACI;wBAjCnC,aAAa;IADzB,IAAA,gBAAM,EAAC,OAAO,CAAC;GACH,aAAa,CAkCzB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.d.ts b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.d.ts new file mode 100644 index 00000000..c93c54d1 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.d.ts @@ -0,0 +1,11 @@ +import { ChatArchiveAggregate } from '../Chat/ChatArchiveAggregate'; +export interface IChatArchiveRepository { + create(archive: Partial): Promise; + findAll(): Promise; + findById(id: string): Promise; + findByChatId(chatId: string): Promise; + findByGameId(gameId: string): Promise; + delete(id: string): Promise; + cleanup(olderThanDays: number): Promise; +} +//# sourceMappingURL=IChatArchiveRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.d.ts.map b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.d.ts.map new file mode 100644 index 00000000..75de25aa --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"IChatArchiveRepository.d.ts","sourceRoot":"","sources":["../../../src/Domain/IRepository/IChatArchiveRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACnC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9E,OAAO,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC3D,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC9D,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC9D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.js b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.js new file mode 100644 index 00000000..2a5ca79f --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IChatArchiveRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.js.map b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.js.map new file mode 100644 index 00000000..d495499a --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatArchiveRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IChatArchiveRepository.js","sourceRoot":"","sources":["../../../src/Domain/IRepository/IChatArchiveRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.d.ts b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.d.ts new file mode 100644 index 00000000..f35d75e9 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.d.ts @@ -0,0 +1,27 @@ +import { ChatAggregate } from '../Chat/ChatAggregate'; +import { ChatArchiveAggregate } from '../Chat/ChatArchiveAggregate'; +export interface IChatRepository { + create(chat: Partial): Promise; + findByPage(from: number, to: number): Promise<{ + chats: ChatAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + chats: ChatAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + findByUserId(userId: string): Promise; + findByUserIdIncludingDeleted(userId: string): Promise; + findByGameId(gameId: string): Promise; + findActiveChatsForUser(userId: string): Promise; + findInactiveChats(inactivityMinutes: number): Promise; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + archiveChat(chat: ChatAggregate): Promise; + getArchivedChat(chatId: string): Promise; + restoreFromArchive(chatId: string): Promise; +} +//# sourceMappingURL=IChatRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.d.ts.map b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.d.ts.map new file mode 100644 index 00000000..926836fc --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"IChatRepository.d.ts","sourceRoot":"","sources":["../../../src/Domain/IRepository/IChatRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACpD,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACpE,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACvD,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACvE,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC5D,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACjE,iBAAiB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACvE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAChE,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACtE,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;CACrE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.js b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.js new file mode 100644 index 00000000..1e002011 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IChatRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.js.map b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.js.map new file mode 100644 index 00000000..23b45337 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IChatRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IChatRepository.js","sourceRoot":"","sources":["../../../src/Domain/IRepository/IChatRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.d.ts b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.d.ts new file mode 100644 index 00000000..0b3a488f --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.d.ts @@ -0,0 +1,20 @@ +import { ContactAggregate } from '../Contact/ContactAggregate'; +export interface IContactRepository { + create(contact: Partial): Promise; + findById(id: string): Promise; + findByPage(from: number, to: number): Promise<{ + contacts: ContactAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + contacts: ContactAggregate[]; + totalCount: number; + }>; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + search(searchTerm: string): Promise; + searchIncludingDeleted(searchTerm: string): Promise; +} +//# sourceMappingURL=IContactRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.d.ts.map b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.d.ts.map new file mode 100644 index 00000000..72778a8d --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"IContactRepository.d.ts","sourceRoot":"","sources":["../../../src/Domain/IRepository/IContactRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IAC/B,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACvD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpG,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACxF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACzD,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACvE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACxD,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CAC3E"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.js b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.js new file mode 100644 index 00000000..df4283f8 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IContactRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.js.map b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.js.map new file mode 100644 index 00000000..4bdbecb2 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IContactRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IContactRepository.js","sourceRoot":"","sources":["../../../src/Domain/IRepository/IContactRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.d.ts b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.d.ts new file mode 100644 index 00000000..79418b01 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.d.ts @@ -0,0 +1,32 @@ +import { DeckAggregate } from '../Deck/DeckAggregate'; +export interface IDeckRepository { + create(deck: Partial): Promise; + findByPage(from: number, to: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + search(query: string, limit?: number, offset?: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + countActiveByUserId(userId: string): Promise; + countOrganizationalByUserId(userId: string): Promise; + findFilteredDecks(userId: string, userOrgId?: string | null, isAdmin?: boolean, from?: number, to?: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; +} +//# sourceMappingURL=IDeckRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.d.ts.map b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.d.ts.map new file mode 100644 index 00000000..f3e16827 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"IDeckRepository.d.ts","sourceRoot":"","sources":["../../../src/Domain/IRepository/IDeckRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACpD,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChH,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChI,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAGtD,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxK"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.js b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.js new file mode 100644 index 00000000..cb1d812d --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IDeckRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.js.map b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.js.map new file mode 100644 index 00000000..0597742e --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IDeckRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IDeckRepository.js","sourceRoot":"","sources":["../../../src/Domain/IRepository/IDeckRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.d.ts b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.d.ts new file mode 100644 index 00000000..b2670168 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.d.ts @@ -0,0 +1,26 @@ +import { OrganizationAggregate } from '../Organization/OrganizationAggregate'; +export interface IOrganizationRepository { + create(org: Partial): Promise; + findByPage(from: number, to: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + search(query: string, limit?: number, offset?: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; +} +//# sourceMappingURL=IOrganizationRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.d.ts.map b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.d.ts.map new file mode 100644 index 00000000..def3f02a --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"IOrganizationRepository.d.ts","sourceRoot":"","sources":["../../../src/Domain/IRepository/IOrganizationRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,MAAM,WAAW,uBAAuB;IACpC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC5E,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9H,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAC5D,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAC5E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChI,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChJ,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAClG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;CACjE"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.js b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.js new file mode 100644 index 00000000..ac3310d4 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IOrganizationRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.js.map b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.js.map new file mode 100644 index 00000000..5aa04dd0 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IOrganizationRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IOrganizationRepository.js","sourceRoot":"","sources":["../../../src/Domain/IRepository/IOrganizationRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.d.ts b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.d.ts new file mode 100644 index 00000000..be74d19f --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.d.ts @@ -0,0 +1,30 @@ +import { UserAggregate } from '../User/UserAggregate'; +export interface IUserRepository { + create(user: Partial): Promise; + findByPage(from: number, to: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + findByUsername(username: string): Promise; + findByEmail(email: string): Promise; + findByToken(token: string): Promise; + search(query: string, limit?: number, offset?: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + deactivate(id: string): Promise; +} +//# sourceMappingURL=IUserRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.d.ts.map b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.d.ts.map new file mode 100644 index 00000000..d0c46fe7 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"IUserRepository.d.ts","sourceRoot":"","sources":["../../../src/Domain/IRepository/IUserRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACpD,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACpE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC1D,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChH,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChI,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACtD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;CACzD"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.js b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.js new file mode 100644 index 00000000..9536e110 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=IUserRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.js.map b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.js.map new file mode 100644 index 00000000..f6ea0182 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/IRepository/IUserRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IUserRepository.js","sourceRoot":"","sources":["../../../src/Domain/IRepository/IUserRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.d.ts b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.d.ts new file mode 100644 index 00000000..4ddbda3b --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.d.ts @@ -0,0 +1,23 @@ +import { UserAggregate } from '../User/UserAggregate'; +export declare const OrganizationState: { + readonly REGISTERED: 0; + readonly ACTIVE: 1; + readonly SOFT_DELETE: 2; +}; +export type OrganizationStateType = typeof OrganizationState[keyof typeof OrganizationState]; +export declare class OrganizationAggregate { + id: string; + name: string; + contactfname: string; + contactlname: string; + contactphone: string; + contactemail: string; + state: OrganizationStateType; + regdate: Date; + updatedate: Date; + url: string | null; + userinorg: number; + maxOrganizationalDecks: number | null; + users: UserAggregate[]; +} +//# sourceMappingURL=OrganizationAggregate.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.d.ts.map b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.d.ts.map new file mode 100644 index 00000000..74d937e5 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationAggregate.d.ts","sourceRoot":"","sources":["../../../src/Domain/Organization/OrganizationAggregate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,eAAO,MAAM,iBAAiB;;;;CAIpB,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAAG,OAAO,iBAAiB,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAE7F,qBACa,qBAAqB;IAE9B,EAAE,EAAG,MAAM,CAAC;IAGZ,IAAI,EAAG,MAAM,CAAC;IAGd,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAGtB,YAAY,EAAG,MAAM,CAAC;IAGtB,KAAK,EAAG,qBAAqB,CAAC;IAG9B,OAAO,EAAG,IAAI,CAAC;IAGf,UAAU,EAAG,IAAI,CAAC;IAGlB,GAAG,EAAG,MAAM,GAAG,IAAI,CAAC;IAGpB,SAAS,EAAG,MAAM,CAAC;IAGnB,sBAAsB,EAAG,MAAM,GAAG,IAAI,CAAC;IAGvC,KAAK,EAAG,aAAa,EAAE,CAAC;CACvB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.js b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.js new file mode 100644 index 00000000..fb171b47 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.js @@ -0,0 +1,78 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OrganizationAggregate = exports.OrganizationState = void 0; +const typeorm_1 = require("typeorm"); +const UserAggregate_1 = require("../User/UserAggregate"); +exports.OrganizationState = { + REGISTERED: 0, + ACTIVE: 1, + SOFT_DELETE: 2 +}; +let OrganizationAggregate = class OrganizationAggregate { +}; +exports.OrganizationAggregate = OrganizationAggregate; +__decorate([ + (0, typeorm_1.PrimaryGeneratedColumn)('uuid'), + __metadata("design:type", String) +], OrganizationAggregate.prototype, "id", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255 }), + __metadata("design:type", String) +], OrganizationAggregate.prototype, "name", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 100 }), + __metadata("design:type", String) +], OrganizationAggregate.prototype, "contactfname", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 100 }), + __metadata("design:type", String) +], OrganizationAggregate.prototype, "contactlname", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 20 }), + __metadata("design:type", String) +], OrganizationAggregate.prototype, "contactphone", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255 }), + __metadata("design:type", String) +], OrganizationAggregate.prototype, "contactemail", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: exports.OrganizationState.REGISTERED }), + __metadata("design:type", Number) +], OrganizationAggregate.prototype, "state", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)(), + __metadata("design:type", Date) +], OrganizationAggregate.prototype, "regdate", void 0); +__decorate([ + (0, typeorm_1.UpdateDateColumn)(), + __metadata("design:type", Date) +], OrganizationAggregate.prototype, "updatedate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: true }), + __metadata("design:type", Object) +], OrganizationAggregate.prototype, "url", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', default: 0 }), + __metadata("design:type", Number) +], OrganizationAggregate.prototype, "userinorg", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'int', nullable: true }), + __metadata("design:type", Object) +], OrganizationAggregate.prototype, "maxOrganizationalDecks", void 0); +__decorate([ + (0, typeorm_1.OneToMany)(() => UserAggregate_1.UserAggregate, user => user.orgid), + __metadata("design:type", Array) +], OrganizationAggregate.prototype, "users", void 0); +exports.OrganizationAggregate = OrganizationAggregate = __decorate([ + (0, typeorm_1.Entity)('Organizations') +], OrganizationAggregate); +//# sourceMappingURL=OrganizationAggregate.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.js.map b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.js.map new file mode 100644 index 00000000..2aa338e1 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/Organization/OrganizationAggregate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationAggregate.js","sourceRoot":"","sources":["../../../src/Domain/Organization/OrganizationAggregate.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAgH;AAChH,yDAAsD;AAEzC,QAAA,iBAAiB,GAAG;IAC7B,UAAU,EAAE,CAAC;IACb,MAAM,EAAE,CAAC;IACT,WAAW,EAAE,CAAC;CACR,CAAC;AAKJ,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CAuC7B,CAAA;AAvCQ,sDAAqB;AAE9B;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;iDACnB;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;mDAC3B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;2DACnB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;2DACnB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;2DAClB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;2DACnB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,yBAAiB,CAAC,UAAU,EAAE,CAAC;;oDACjC;AAG9B;IADC,IAAA,0BAAgB,GAAE;8BACT,IAAI;sDAAC;AAGf;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;yDAAC;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACrC;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;wDACjB;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qEACD;AAGvC;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,6BAAa,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;;oDAC3B;gCAtCf,qBAAqB;IADjC,IAAA,gBAAM,EAAC,eAAe,CAAC;GACX,qBAAqB,CAuC7B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/User/UserAggregate.d.ts b/SerpentRace_Backend/dist/Domain/User/UserAggregate.d.ts new file mode 100644 index 00000000..1ee37a1e --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/User/UserAggregate.d.ts @@ -0,0 +1,26 @@ +export declare enum UserState { + REGISTERED_NOT_VERIFIED = 0, + VERIFIED_REGULAR = 1, + VERIFIED_PREMIUM = 2, + SOFT_DELETE = 3, + DEACTIVATED = 4, + ADMIN = 5 +} +export declare class UserAggregate { + id: string; + orgid: string | null; + username: string; + password: string; + email: string; + fname: string; + lname: string; + token: string | null; + TokenExpires: Date | null; + type: string; + phone: string | null; + state: UserState; + regdate: Date; + updatedate: Date; + Orglogindate: Date | null; +} +//# sourceMappingURL=UserAggregate.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/User/UserAggregate.d.ts.map b/SerpentRace_Backend/dist/Domain/User/UserAggregate.d.ts.map new file mode 100644 index 00000000..dedef4a4 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/User/UserAggregate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UserAggregate.d.ts","sourceRoot":"","sources":["../../../src/Domain/User/UserAggregate.ts"],"names":[],"mappings":"AAEA,oBAAY,SAAS;IACjB,uBAAuB,IAAI;IAC3B,gBAAgB,IAAI;IACpB,gBAAgB,IAAI;IACpB,WAAW,IAAI;IACf,WAAW,IAAI;IACf,KAAK,IAAI;CACZ;AAED,qBACa,aAAa;IAEtB,EAAE,EAAG,MAAM,CAAC;IAGZ,KAAK,EAAG,MAAM,GAAG,IAAI,CAAC;IAGtB,QAAQ,EAAG,MAAM,CAAC;IAGlB,QAAQ,EAAG,MAAM,CAAC;IAGlB,KAAK,EAAG,MAAM,CAAC;IAGf,KAAK,EAAG,MAAM,CAAC;IAGf,KAAK,EAAG,MAAM,CAAC;IAGf,KAAK,EAAG,MAAM,GAAG,IAAI,CAAC;IAGtB,YAAY,EAAG,IAAI,GAAG,IAAI,CAAC;IAG3B,IAAI,EAAG,MAAM,CAAC;IAGd,KAAK,EAAG,MAAM,GAAG,IAAI,CAAC;IAMtB,KAAK,EAAG,SAAS,CAAC;IAGlB,OAAO,EAAG,IAAI,CAAC;IAGf,UAAU,EAAG,IAAI,CAAC;IAGlB,YAAY,EAAG,IAAI,GAAG,IAAI,CAAC;CAC9B"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/User/UserAggregate.js b/SerpentRace_Backend/dist/Domain/User/UserAggregate.js new file mode 100644 index 00000000..d9f65bba --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/User/UserAggregate.js @@ -0,0 +1,92 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserAggregate = exports.UserState = void 0; +const typeorm_1 = require("typeorm"); +var UserState; +(function (UserState) { + UserState[UserState["REGISTERED_NOT_VERIFIED"] = 0] = "REGISTERED_NOT_VERIFIED"; + UserState[UserState["VERIFIED_REGULAR"] = 1] = "VERIFIED_REGULAR"; + UserState[UserState["VERIFIED_PREMIUM"] = 2] = "VERIFIED_PREMIUM"; + UserState[UserState["SOFT_DELETE"] = 3] = "SOFT_DELETE"; + UserState[UserState["DEACTIVATED"] = 4] = "DEACTIVATED"; + UserState[UserState["ADMIN"] = 5] = "ADMIN"; +})(UserState || (exports.UserState = UserState = {})); +let UserAggregate = class UserAggregate { +}; +exports.UserAggregate = UserAggregate; +__decorate([ + (0, typeorm_1.PrimaryGeneratedColumn)('uuid'), + __metadata("design:type", String) +], UserAggregate.prototype, "id", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'uuid', nullable: true }), + __metadata("design:type", Object) +], UserAggregate.prototype, "orgid", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 100, unique: true }), + __metadata("design:type", String) +], UserAggregate.prototype, "username", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255 }), + __metadata("design:type", String) +], UserAggregate.prototype, "password", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255, unique: true }), + __metadata("design:type", String) +], UserAggregate.prototype, "email", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 100 }), + __metadata("design:type", String) +], UserAggregate.prototype, "fname", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 100 }), + __metadata("design:type", String) +], UserAggregate.prototype, "lname", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }), + __metadata("design:type", Object) +], UserAggregate.prototype, "token", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'timestamp', nullable: true }), + __metadata("design:type", Object) +], UserAggregate.prototype, "TokenExpires", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 50 }), + __metadata("design:type", String) +], UserAggregate.prototype, "type", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'varchar', length: 20, nullable: true }), + __metadata("design:type", Object) +], UserAggregate.prototype, "phone", void 0); +__decorate([ + (0, typeorm_1.Column)({ + type: 'int', + default: UserState.REGISTERED_NOT_VERIFIED + }), + __metadata("design:type", Number) +], UserAggregate.prototype, "state", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)(), + __metadata("design:type", Date) +], UserAggregate.prototype, "regdate", void 0); +__decorate([ + (0, typeorm_1.UpdateDateColumn)(), + __metadata("design:type", Date) +], UserAggregate.prototype, "updatedate", void 0); +__decorate([ + (0, typeorm_1.Column)({ type: 'timestamp', nullable: true }), + __metadata("design:type", Object) +], UserAggregate.prototype, "Orglogindate", void 0); +exports.UserAggregate = UserAggregate = __decorate([ + (0, typeorm_1.Entity)('Users') +], UserAggregate); +//# sourceMappingURL=UserAggregate.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Domain/User/UserAggregate.js.map b/SerpentRace_Backend/dist/Domain/User/UserAggregate.js.map new file mode 100644 index 00000000..0f0b2cb4 --- /dev/null +++ b/SerpentRace_Backend/dist/Domain/User/UserAggregate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UserAggregate.js","sourceRoot":"","sources":["../../../src/Domain/User/UserAggregate.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAqG;AAErG,IAAY,SAOX;AAPD,WAAY,SAAS;IACjB,+EAA2B,CAAA;IAC3B,iEAAoB,CAAA;IACpB,iEAAoB,CAAA;IACpB,uDAAe,CAAA;IACf,uDAAe,CAAA;IACf,2CAAS,CAAA;AACb,CAAC,EAPW,SAAS,yBAAT,SAAS,QAOpB;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAgDzB,CAAA;AAhDY,sCAAa;AAEtB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;yCACnB;AAGZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACnB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;;+CACrC;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;+CACvB;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;;4CACxC;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;4CAC1B;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;4CAC1B;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACnC;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACnB;AAG3B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;2CAC1B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CAClC;AAMtB;IAJC,IAAA,gBAAM,EAAC;QACJ,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,SAAS,CAAC,uBAAuB;KAC7C,CAAC;;4CACgB;AAGlB;IADC,IAAA,0BAAgB,GAAE;8BACT,IAAI;8CAAC;AAGf;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;iDAAC;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACnB;wBA/ClB,aAAa;IADzB,IAAA,gBAAM,EAAC,OAAO,CAAC;GACH,aAAa,CAgDzB"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.d.ts new file mode 100644 index 00000000..daeeae80 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.d.ts @@ -0,0 +1,7 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class Test1755691733404 implements MigrationInterface { + name: string; + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755691733404-test.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.d.ts.map new file mode 100644 index 00000000..54363f50 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755691733404-test.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755691733404-test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,iBAAkB,YAAW,kBAAkB;IACxD,IAAI,SAAsB;IAEb,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.js b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.js new file mode 100644 index 00000000..c1233c26 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Test1755691733404 = void 0; +class Test1755691733404 { + constructor() { + this.name = 'Test1755691733404'; + } + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "Users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "orgid" uuid, "username" character varying(100) NOT NULL, "password" character varying(255) NOT NULL, "email" character varying(255) NOT NULL, "fname" character varying(100) NOT NULL, "lname" character varying(100) NOT NULL, "code" character varying(50), "type" character varying(50) NOT NULL, "phone" character varying(20), "state" integer NOT NULL DEFAULT '0', "regdate" TIMESTAMP NOT NULL DEFAULT now(), "updatedate" TIMESTAMP NOT NULL DEFAULT now(), "Orglogindate" TIMESTAMP, CONSTRAINT "UQ_ffc81a3b97dcbf8e320d5106c0d" UNIQUE ("username"), CONSTRAINT "UQ_3c3ab3f49a87e6ddb607f3c4945" UNIQUE ("email"), CONSTRAINT "PK_16d4f7d636df336db11d87413e3" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "Organizations" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "contactfname" character varying(100) NOT NULL, "contactlname" character varying(100) NOT NULL, "contactphone" character varying(20) NOT NULL, "contactemail" character varying(255) NOT NULL, "state" integer NOT NULL DEFAULT '0', "regdate" TIMESTAMP NOT NULL DEFAULT now(), "updatedate" TIMESTAMP NOT NULL DEFAULT now(), "url" character varying(500), "userinorg" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_e0690a31419f6666194423526f2" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "Decks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "type" integer NOT NULL, "user_id" uuid NOT NULL, "creation_date" TIMESTAMP NOT NULL DEFAULT now(), "cards" json NOT NULL, "played_number" integer NOT NULL DEFAULT '0', "ctype" integer NOT NULL DEFAULT '0', "update_date" TIMESTAMP NOT NULL DEFAULT now(), "state" integer NOT NULL DEFAULT '0', "organization_id" uuid, CONSTRAINT "PK_001f26cb3ec39c1f25269943473" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "Chats" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "users" uuid array NOT NULL, "messages" json NOT NULL, "updateDate" TIMESTAMP NOT NULL DEFAULT now(), "state" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_64c36c2b8d86a0d5de4cf64de8d" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TABLE "Decks" ADD CONSTRAINT "FK_06ee28f90d68543a03b14aebe13" FOREIGN KEY ("organization_id") REFERENCES "Organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "Decks" DROP CONSTRAINT "FK_06ee28f90d68543a03b14aebe13"`); + await queryRunner.query(`DROP TABLE "Chats"`); + await queryRunner.query(`DROP TABLE "Decks"`); + await queryRunner.query(`DROP TABLE "Organizations"`); + await queryRunner.query(`DROP TABLE "Users"`); + } +} +exports.Test1755691733404 = Test1755691733404; +//# sourceMappingURL=1755691733404-test.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.js.map new file mode 100644 index 00000000..35516751 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755691733404-test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755691733404-test.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755691733404-test.ts"],"names":[],"mappings":";;;AAEA,MAAa,iBAAiB;IAA9B;QACI,SAAI,GAAG,mBAAmB,CAAA;IAkB9B,CAAC;IAhBU,KAAK,CAAC,EAAE,CAAC,WAAwB;QACpC,MAAM,WAAW,CAAC,KAAK,CAAC,quBAAquB,CAAC,CAAC;QAC/vB,MAAM,WAAW,CAAC,KAAK,CAAC,8jBAA8jB,CAAC,CAAC;QACxlB,MAAM,WAAW,CAAC,KAAK,CAAC,2eAA2e,CAAC,CAAC;QACrgB,MAAM,WAAW,CAAC,KAAK,CAAC,kRAAkR,CAAC,CAAC;QAC5S,MAAM,WAAW,CAAC,KAAK,CAAC,8KAA8K,CAAC,CAAC;IAC5M,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACtC,MAAM,WAAW,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAChG,MAAM,WAAW,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC9C,MAAM,WAAW,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC9C,MAAM,WAAW,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACtD,MAAM,WAAW,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAClD,CAAC;CAEJ;AAnBD,8CAmBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.d.ts new file mode 100644 index 00000000..4610b935 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.d.ts @@ -0,0 +1,7 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class AddEmailVerificationFields1755706019351 implements MigrationInterface { + name: string; + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755706019351-AddEmailVerificationFields.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.d.ts.map new file mode 100644 index 00000000..d0839321 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755706019351-AddEmailVerificationFields.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,uCAAwC,YAAW,kBAAkB;IAC9E,IAAI,SAA4C;IAEnC,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAM7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.js b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.js new file mode 100644 index 00000000..c511a32f --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AddEmailVerificationFields1755706019351 = void 0; +class AddEmailVerificationFields1755706019351 { + constructor() { + this.name = 'AddEmailVerificationFields1755706019351'; + } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "Users" DROP COLUMN "code"`); + await queryRunner.query(`ALTER TABLE "Users" ADD "token" character varying(255)`); + await queryRunner.query(`ALTER TABLE "Users" ADD "TokenExpires" TIMESTAMP`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "Users" DROP COLUMN "TokenExpires"`); + await queryRunner.query(`ALTER TABLE "Users" DROP COLUMN "token"`); + await queryRunner.query(`ALTER TABLE "Users" ADD "code" character varying(50)`); + } +} +exports.AddEmailVerificationFields1755706019351 = AddEmailVerificationFields1755706019351; +//# sourceMappingURL=1755706019351-AddEmailVerificationFields.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.js.map new file mode 100644 index 00000000..1f1b7cc4 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755706019351-AddEmailVerificationFields.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.ts"],"names":[],"mappings":";;;AAEA,MAAa,uCAAuC;IAApD;QACI,SAAI,GAAG,yCAAyC,CAAA;IAcpD,CAAC;IAZU,KAAK,CAAC,EAAE,CAAC,WAAwB;QACpC,MAAM,WAAW,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,WAAW,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAClF,MAAM,WAAW,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAChF,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACtC,MAAM,WAAW,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC1E,MAAM,WAAW,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACnE,MAAM,WAAW,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;IACpF,CAAC;CAEJ;AAfD,0FAeC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.d.ts new file mode 100644 index 00000000..2ab27faa --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.d.ts @@ -0,0 +1,7 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class AddChatMessagingSystem1755817306222 implements MigrationInterface { + name: string; + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755817306222-AddChatMessagingSystem.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.d.ts.map new file mode 100644 index 00000000..5d713781 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755817306222-AddChatMessagingSystem.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,mCAAoC,YAAW,kBAAkB;IAC1E,IAAI,SAAwC;IAE/B,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAY7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.js b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.js new file mode 100644 index 00000000..511fb906 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AddChatMessagingSystem1755817306222 = void 0; +class AddChatMessagingSystem1755817306222 { + constructor() { + this.name = 'AddChatMessagingSystem1755817306222'; + } + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "ChatArchives" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "chatId" uuid NOT NULL, "archivedMessages" json NOT NULL, "archivedAt" TIMESTAMP NOT NULL, "createDate" TIMESTAMP NOT NULL DEFAULT now(), "chatType" character varying(50) NOT NULL, "chatName" character varying(255), "gameId" uuid, "participants" uuid array NOT NULL, CONSTRAINT "PK_fe62979fc2061d7afe278d3f14e" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "type" character varying(50) NOT NULL DEFAULT 'direct'`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "name" character varying(255)`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "gameId" uuid`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "createdBy" uuid`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "lastActivity" TIMESTAMP`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "createDate" TIMESTAMP NOT NULL DEFAULT now()`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "archiveDate" TIMESTAMP`); + await queryRunner.query(`ALTER TABLE "Chats" ALTER COLUMN "messages" SET DEFAULT '[]'`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "Chats" ALTER COLUMN "messages" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "archiveDate"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "createDate"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "lastActivity"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "createdBy"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "gameId"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "name"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "type"`); + await queryRunner.query(`DROP TABLE "ChatArchives"`); + } +} +exports.AddChatMessagingSystem1755817306222 = AddChatMessagingSystem1755817306222; +//# sourceMappingURL=1755817306222-AddChatMessagingSystem.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.js.map new file mode 100644 index 00000000..c011dcbf --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755817306222-AddChatMessagingSystem.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.ts"],"names":[],"mappings":";;;AAEA,MAAa,mCAAmC;IAAhD;QACI,SAAI,GAAG,qCAAqC,CAAA;IA0BhD,CAAC;IAxBU,KAAK,CAAC,EAAE,CAAC,WAAwB;QACpC,MAAM,WAAW,CAAC,KAAK,CAAC,wZAAwZ,CAAC,CAAC;QAClb,MAAM,WAAW,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAC;QAC1G,MAAM,WAAW,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACjF,MAAM,WAAW,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACjE,MAAM,WAAW,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpE,MAAM,WAAW,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAC5E,MAAM,WAAW,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;QACjG,MAAM,WAAW,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QAC3E,MAAM,WAAW,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAC5F,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACtC,MAAM,WAAW,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;QACpF,MAAM,WAAW,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACzE,MAAM,WAAW,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACxE,MAAM,WAAW,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC1E,MAAM,WAAW,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACvE,MAAM,WAAW,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpE,MAAM,WAAW,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,WAAW,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,WAAW,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACzD,CAAC;CAEJ;AA3BD,kFA2BC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.d.ts new file mode 100644 index 00000000..cbe2f1c0 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.d.ts @@ -0,0 +1,7 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class CreateContactTable1755855028839 implements MigrationInterface { + name: string; + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755855028839-CreateContactTable.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.d.ts.map new file mode 100644 index 00000000..54ebe71b --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755855028839-CreateContactTable.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755855028839-CreateContactTable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,+BAAgC,YAAW,kBAAkB;IACtE,IAAI,SAAoC;IAE3B,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAI7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.js b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.js new file mode 100644 index 00000000..4107cbb1 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateContactTable1755855028839 = void 0; +class CreateContactTable1755855028839 { + constructor() { + this.name = 'CreateContactTable1755855028839'; + } + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "Contacts" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "email" character varying(255) NOT NULL, "userid" uuid, "type" integer NOT NULL, "txt" text NOT NULL, "state" integer NOT NULL DEFAULT '0', "createDate" TIMESTAMP NOT NULL DEFAULT now(), "updateDate" TIMESTAMP NOT NULL DEFAULT now(), "adminResponse" text, "responseDate" TIMESTAMP, "respondedBy" uuid, CONSTRAINT "PK_68782cec65c8eef577c62958273" PRIMARY KEY ("id"))`); + } + async down(queryRunner) { + await queryRunner.query(`DROP TABLE "Contacts"`); + } +} +exports.CreateContactTable1755855028839 = CreateContactTable1755855028839; +//# sourceMappingURL=1755855028839-CreateContactTable.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.js.map new file mode 100644 index 00000000..397f15a4 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755855028839-CreateContactTable.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755855028839-CreateContactTable.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755855028839-CreateContactTable.ts"],"names":[],"mappings":";;;AAEA,MAAa,+BAA+B;IAA5C;QACI,SAAI,GAAG,iCAAiC,CAAA;IAU5C,CAAC;IARU,KAAK,CAAC,EAAE,CAAC,WAAwB;QACpC,MAAM,WAAW,CAAC,KAAK,CAAC,+dAA+d,CAAC,CAAC;IAC7f,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACtC,MAAM,WAAW,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACrD,CAAC;CAEJ;AAXD,0EAWC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.d.ts new file mode 100644 index 00000000..c365cdad --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.d.ts @@ -0,0 +1 @@ +//# sourceMappingURL=1755905000000-AddStateToChatArchives.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.d.ts.map new file mode 100644 index 00000000..74132976 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755905000000-AddStateToChatArchives.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.js b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.js new file mode 100644 index 00000000..b60c2dd0 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.js @@ -0,0 +1,2 @@ +"use strict"; +//# sourceMappingURL=1755905000000-AddStateToChatArchives.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.js.map new file mode 100644 index 00000000..39610d8b --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755905000000-AddStateToChatArchives.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.d.ts new file mode 100644 index 00000000..d1891039 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.d.ts @@ -0,0 +1,7 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +export declare class AddMaxOrganizationalDecksToOrganization1692712800000 implements MigrationInterface { + name: string; + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=AddMaxOrganizationalDecksToOrganization.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.d.ts.map new file mode 100644 index 00000000..6056d793 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"AddMaxOrganizationalDecksToOrganization.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAEvE,qBAAa,oDAAqD,YAAW,kBAAkB;IAC3F,IAAI,SAA0D;IAEjD,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.js b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.js new file mode 100644 index 00000000..35e0a2f0 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AddMaxOrganizationalDecksToOrganization1692712800000 = void 0; +const typeorm_1 = require("typeorm"); +class AddMaxOrganizationalDecksToOrganization1692712800000 { + constructor() { + this.name = 'AddMaxOrganizationalDecksToOrganization1692712800000'; + } + async up(queryRunner) { + // Add maxOrganizationalDecks column to Organizations table + await queryRunner.addColumn('Organizations', new typeorm_1.TableColumn({ + name: 'maxOrganizationalDecks', + type: 'int', + isNullable: true, // No default - set by admin + comment: 'Maximum number of organizational decks a premium user can create in this organization' + })); + // Add performance indexes for deck filtering queries + await queryRunner.query(`CREATE INDEX "IDX_DECK_USER_STATE_CTYPE" ON "Decks" ("user_id", "state", "ctype")`); + await queryRunner.query(`CREATE INDEX "IDX_DECK_ORG_CTYPE_STATE" ON "Decks" ("organization_id", "ctype", "state")`); + } + async down(queryRunner) { + // Drop indexes + await queryRunner.query(`DROP INDEX "IDX_DECK_ORG_CTYPE_STATE"`); + await queryRunner.query(`DROP INDEX "IDX_DECK_USER_STATE_CTYPE"`); + // Remove maxOrganizationalDecks column + await queryRunner.dropColumn('Organizations', 'maxOrganizationalDecks'); + } +} +exports.AddMaxOrganizationalDecksToOrganization1692712800000 = AddMaxOrganizationalDecksToOrganization1692712800000; +//# sourceMappingURL=AddMaxOrganizationalDecksToOrganization.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.js.map new file mode 100644 index 00000000..e4cf7c4a --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AddMaxOrganizationalDecksToOrganization.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.ts"],"names":[],"mappings":";;;AAAA,qCAAuE;AAEvE,MAAa,oDAAoD;IAAjE;QACI,SAAI,GAAG,sDAAsD,CAAC;IAwBlE,CAAC;IAtBU,KAAK,CAAC,EAAE,CAAC,WAAwB;QACpC,2DAA2D;QAC3D,MAAM,WAAW,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,qBAAW,CAAC;YACzD,IAAI,EAAE,wBAAwB;YAC9B,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,IAAI,EAAE,4BAA4B;YAC9C,OAAO,EAAE,uFAAuF;SACnG,CAAC,CAAC,CAAC;QAEJ,qDAAqD;QACrD,MAAM,WAAW,CAAC,KAAK,CAAC,mFAAmF,CAAC,CAAC;QAC7G,MAAM,WAAW,CAAC,KAAK,CAAC,0FAA0F,CAAC,CAAC;IACxH,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACtC,eAAe;QACf,MAAM,WAAW,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACjE,MAAM,WAAW,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAElE,uCAAuC;QACvC,MAAM,WAAW,CAAC,UAAU,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAC5E,CAAC;CACJ;AAzBD,oHAyBC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.d.ts new file mode 100644 index 00000000..da0ed125 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.d.ts @@ -0,0 +1,6 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class Test1755691732089 implements MigrationInterface { + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755691732089-test.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.d.ts.map new file mode 100644 index 00000000..b07ad7c1 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755691732089-test.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrationsettings/1755691732089-test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,iBAAkB,YAAW,kBAAkB;IAE3C,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.js b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.js new file mode 100644 index 00000000..fee391e3 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Test1755691732089 = void 0; +class Test1755691732089 { + async up(queryRunner) { + } + async down(queryRunner) { + } +} +exports.Test1755691732089 = Test1755691732089; +//# sourceMappingURL=1755691732089-test.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.js.map new file mode 100644 index 00000000..2497b8a9 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755691732089-test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755691732089-test.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrationsettings/1755691732089-test.ts"],"names":[],"mappings":";;;AAEA,MAAa,iBAAiB;IAEnB,KAAK,CAAC,EAAE,CAAC,WAAwB;IACxC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;IAC1C,CAAC;CAEJ;AARD,8CAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.d.ts new file mode 100644 index 00000000..c26bc0ca --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.d.ts @@ -0,0 +1,6 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class AddEmailVerificationFields1755706017175 implements MigrationInterface { + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755706017175-AddEmailVerificationFields.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.d.ts.map new file mode 100644 index 00000000..c0aebeb2 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755706017175-AddEmailVerificationFields.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,uCAAwC,YAAW,kBAAkB;IAEjE,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.js b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.js new file mode 100644 index 00000000..62e0548b --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AddEmailVerificationFields1755706017175 = void 0; +class AddEmailVerificationFields1755706017175 { + async up(queryRunner) { + } + async down(queryRunner) { + } +} +exports.AddEmailVerificationFields1755706017175 = AddEmailVerificationFields1755706017175; +//# sourceMappingURL=1755706017175-AddEmailVerificationFields.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.js.map new file mode 100644 index 00000000..87bdbb9c --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755706017175-AddEmailVerificationFields.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.ts"],"names":[],"mappings":";;;AAEA,MAAa,uCAAuC;IAEzC,KAAK,CAAC,EAAE,CAAC,WAAwB;IACxC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;IAC1C,CAAC;CAEJ;AARD,0FAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.d.ts b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.d.ts new file mode 100644 index 00000000..2d2689a3 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.d.ts @@ -0,0 +1,6 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +export declare class FixEmailVerificationFields1755706055220 implements MigrationInterface { + up(queryRunner: QueryRunner): Promise; + down(queryRunner: QueryRunner): Promise; +} +//# sourceMappingURL=1755706055220-FixEmailVerificationFields.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.d.ts.map new file mode 100644 index 00000000..b61f4377 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"1755706055220-FixEmailVerificationFields.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,uCAAwC,YAAW,kBAAkB;IAEjE,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7D"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.js b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.js new file mode 100644 index 00000000..f2be8d40 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FixEmailVerificationFields1755706055220 = void 0; +class FixEmailVerificationFields1755706055220 { + async up(queryRunner) { + } + async down(queryRunner) { + } +} +exports.FixEmailVerificationFields1755706055220 = FixEmailVerificationFields1755706055220; +//# sourceMappingURL=1755706055220-FixEmailVerificationFields.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.js.map b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.js.map new file mode 100644 index 00000000..7afe3c49 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.js.map @@ -0,0 +1 @@ +{"version":3,"file":"1755706055220-FixEmailVerificationFields.js","sourceRoot":"","sources":["../../../src/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.ts"],"names":[],"mappings":";;;AAEA,MAAa,uCAAuC;IAEzC,KAAK,CAAC,EAAE,CAAC,WAAwB;IACxC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;IAC1C,CAAC;CAEJ;AARD,0FAQC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.d.ts b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.d.ts new file mode 100644 index 00000000..a400d7ed --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.d.ts @@ -0,0 +1,14 @@ +import { ChatArchiveAggregate } from '../../Domain/Chat/ChatArchiveAggregate'; +import { IChatArchiveRepository } from '../../Domain/IRepository/IChatArchiveRepository'; +export declare class ChatArchiveRepository implements IChatArchiveRepository { + private repo; + constructor(); + create(archive: Partial): Promise & ChatArchiveAggregate>; + findAll(): Promise; + findById(id: string): Promise; + findByChatId(chatId: string): Promise; + findByGameId(gameId: string): Promise; + delete(id: string): Promise; + cleanup(olderThanDays: number): Promise; +} +//# sourceMappingURL=ChatArchiveRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.d.ts.map new file mode 100644 index 00000000..c792b29f --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatArchiveRepository.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/ChatArchiveRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AAIzF,qBAAa,qBAAsB,YAAW,sBAAsB;IAChE,OAAO,CAAC,IAAI,CAAmC;;IAMzC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC;IAgB7C,OAAO;IAcP,QAAQ,CAAC,EAAE,EAAE,MAAM;IAenB,YAAY,CAAC,MAAM,EAAE,MAAM;IAoB3B,YAAY,CAAC,MAAM,EAAE,MAAM;IAoB3B,MAAM,CAAC,EAAE,EAAE,MAAM;IAejB,OAAO,CAAC,aAAa,EAAE,MAAM;CAuBtC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.js b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.js new file mode 100644 index 00000000..68abaf27 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.js @@ -0,0 +1,132 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatArchiveRepository = void 0; +const ormconfig_1 = require("../ormconfig"); +const ChatArchiveAggregate_1 = require("../../Domain/Chat/ChatArchiveAggregate"); +const Logger_1 = require("../../Application/Services/Logger"); +class ChatArchiveRepository { + constructor() { + this.repo = ormconfig_1.AppDataSource.getRepository(ChatArchiveAggregate_1.ChatArchiveAggregate); + } + async create(archive) { + const startTime = Date.now(); + try { + const result = await this.repo.save(archive); + (0, Logger_1.logDatabase)('Chat archive created successfully', undefined, Date.now() - startTime, { + archiveId: result.id, + chatId: result.chatId, + messageCount: result.archivedMessages?.length || 0 + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.create error', error); + throw new Error('Failed to create chat archive in database'); + } + } + async findAll() { + const startTime = Date.now(); + try { + const result = await this.repo.find(); + (0, Logger_1.logDatabase)('All chat archives retrieved', undefined, Date.now() - startTime, { + count: result.length + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.findAll error', error); + throw new Error('Failed to retrieve chat archives from database'); + } + } + async findById(id) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ id }); + (0, Logger_1.logDatabase)('Chat archive retrieved by id', `findById(${id})`, Date.now() - startTime, { + archiveId: id, + found: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.findById error', error); + throw new Error('Failed to find chat archive by id'); + } + } + async findByChatId(chatId) { + const startTime = Date.now(); + try { + const result = await this.repo + .find({ + where: { chatId }, + order: { archivedAt: 'DESC' } + }); + (0, Logger_1.logDatabase)('Chat archives retrieved by chat id', `findByChatId(${chatId})`, Date.now() - startTime, { + chatId, + count: result.length + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.findByChatId error', error); + throw new Error('Failed to find chat archives by chat id'); + } + } + async findByGameId(gameId) { + const startTime = Date.now(); + try { + const result = await this.repo + .find({ + where: { gameId }, + order: { archivedAt: 'DESC' } + }); + (0, Logger_1.logDatabase)('Chat archives retrieved by game id', `findByGameId(${gameId})`, Date.now() - startTime, { + gameId, + count: result.length + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.findByGameId error', error); + throw new Error('Failed to find chat archives by game id'); + } + } + async delete(id) { + const startTime = Date.now(); + try { + const result = await this.repo.delete(id); + (0, Logger_1.logDatabase)('Chat archive deleted', `delete(${id})`, Date.now() - startTime, { + archiveId: id, + affected: result.affected + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.delete error', error); + throw new Error('Failed to delete chat archive'); + } + } + async cleanup(olderThanDays) { + const startTime = Date.now(); + try { + const cutoffDate = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000); + const result = await this.repo + .createQueryBuilder() + .delete() + .where('archivedAt < :cutoffDate', { cutoffDate }) + .execute(); + (0, Logger_1.logDatabase)('Chat archive cleanup completed', `cleanup(${olderThanDays} days)`, Date.now() - startTime, { + olderThanDays, + deleted: result.affected, + cutoffDate + }); + return result.affected || 0; + } + catch (error) { + (0, Logger_1.logError)('ChatArchiveRepository.cleanup error', error); + throw new Error('Failed to cleanup old chat archives'); + } + } +} +exports.ChatArchiveRepository = ChatArchiveRepository; +//# sourceMappingURL=ChatArchiveRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.js.map b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.js.map new file mode 100644 index 00000000..e8e74d18 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatArchiveRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatArchiveRepository.js","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/ChatArchiveRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.d.ts b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.d.ts new file mode 100644 index 00000000..a4c82c8e --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.d.ts @@ -0,0 +1,31 @@ +import { ChatAggregate } from '../../Domain/Chat/ChatAggregate'; +import { ChatArchiveAggregate } from '../../Domain/Chat/ChatArchiveAggregate'; +import { IChatRepository } from '../../Domain/IRepository/IChatRepository'; +export declare class ChatRepository implements IChatRepository { + private repo; + private archiveRepo; + constructor(); + create(chat: Partial): Promise & ChatAggregate>; + findByPage(from: number, to: number): Promise<{ + chats: ChatAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + chats: ChatAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + findByUserId(userId: string): Promise; + findByUserIdIncludingDeleted(userId: string): Promise; + findByGameId(gameId: string): Promise; + findActiveChatsForUser(userId: string): Promise; + findInactiveChats(inactivityMinutes: number): Promise; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + archiveChat(chat: ChatAggregate): Promise; + getArchivedChat(chatId: string): Promise; + restoreFromArchive(chatId: string): Promise; +} +//# sourceMappingURL=ChatRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.d.ts.map new file mode 100644 index 00000000..6274573f --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatRepository.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/ChatRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAuB,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAG3E,qBAAa,cAAe,YAAW,eAAe;IAClD,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,WAAW,CAAmC;;IAOhD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC;IAgBnC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA2B7F,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA0B7G,QAAQ,CAAC,EAAE,EAAE,MAAM;IAoBnB,wBAAwB,CAAC,EAAE,EAAE,MAAM;IAenC,YAAY,CAAC,MAAM,EAAE,MAAM;IAoB3B,4BAA4B,CAAC,MAAM,EAAE,MAAM;IAmB3C,YAAY,CAAC,MAAM,EAAE,MAAM;IAmB3B,sBAAsB,CAAC,MAAM,EAAE,MAAM;IAqBrC,iBAAiB,CAAC,iBAAiB,EAAE,MAAM;IAuB3C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC;IAiBjD,MAAM,CAAC,EAAE,EAAE,MAAM;IAejB,UAAU,CAAC,EAAE,EAAE,MAAM;IAgBrB,WAAW,CAAC,IAAI,EAAE,aAAa;IAiC/B,eAAe,CAAC,MAAM,EAAE,MAAM;IAe9B,kBAAkB,CAAC,MAAM,EAAE,MAAM;CAuC1C"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.js b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.js new file mode 100644 index 00000000..c782b107 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.js @@ -0,0 +1,339 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatRepository = void 0; +const typeorm_1 = require("typeorm"); +const ormconfig_1 = require("../ormconfig"); +const ChatAggregate_1 = require("../../Domain/Chat/ChatAggregate"); +const ChatArchiveAggregate_1 = require("../../Domain/Chat/ChatArchiveAggregate"); +const Logger_1 = require("../../Application/Services/Logger"); +class ChatRepository { + constructor() { + this.repo = ormconfig_1.AppDataSource.getRepository(ChatAggregate_1.ChatAggregate); + this.archiveRepo = ormconfig_1.AppDataSource.getRepository(ChatArchiveAggregate_1.ChatArchiveAggregate); + } + async create(chat) { + const startTime = Date.now(); + try { + const result = await this.repo.save(chat); + (0, Logger_1.logDatabase)('Chat created successfully', undefined, Date.now() - startTime, { + chatId: result.id, + type: result.type, + participants: result.users?.length || 0 + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.create error', error); + throw new Error('Failed to create chat in database'); + } + } + async findByPage(from, to) { + const startTime = Date.now(); + try { + const skip = from; + const take = to - from + 1; + const [chats, totalCount] = await this.repo.findAndCount({ + where: { state: (0, typeorm_1.Not)(ChatAggregate_1.ChatState.SOFT_DELETE) }, + order: { createDate: 'DESC' }, + skip, + take + }); + (0, Logger_1.logDatabase)('Chats page retrieved successfully', undefined, Date.now() - startTime, { + from, + to, + returned: chats.length, + totalCount + }); + return { chats, totalCount }; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findByPage error', error); + throw new Error('Failed to retrieve chats page from database'); + } + } + async findByPageIncludingDeleted(from, to) { + const startTime = Date.now(); + try { + const skip = from; + const take = to - from + 1; + const [chats, totalCount] = await this.repo.findAndCount({ + order: { createDate: 'DESC' }, + skip, + take + }); + (0, Logger_1.logDatabase)('Chats page retrieved successfully (including deleted)', undefined, Date.now() - startTime, { + from, + to, + returned: chats.length, + totalCount + }); + return { chats, totalCount }; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findByPageIncludingDeleted error', error); + throw new Error('Failed to retrieve chats page from database'); + } + } + async findById(id) { + const startTime = Date.now(); + try { + const result = await this.repo.findOne({ + where: { + id, + state: (0, typeorm_1.Not)(ChatAggregate_1.ChatState.SOFT_DELETE) + } + }); + (0, Logger_1.logDatabase)('Chat findById query completed', undefined, Date.now() - startTime, { + found: !!result, + chatId: id + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findById error', error); + throw new Error('Failed to retrieve chat from database'); + } + } + async findByIdIncludingDeleted(id) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ id }); + (0, Logger_1.logDatabase)('Chat findByIdIncludingDeleted query completed', undefined, Date.now() - startTime, { + found: !!result, + chatId: id + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findByIdIncludingDeleted error', error); + throw new Error('Failed to retrieve chat from database'); + } + } + async findByUserId(userId) { + const startTime = Date.now(); + try { + const result = await this.repo + .createQueryBuilder('chat') + .where(':userId = ANY(chat.users)', { userId }) + .andWhere('chat.state != :softDelete', { softDelete: ChatAggregate_1.ChatState.SOFT_DELETE }) + .getMany(); + (0, Logger_1.logDatabase)('Chats retrieved by user id', `findByUserId(${userId})`, Date.now() - startTime, { + userId, + count: result.length + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findByUserId error', error); + throw new Error('Failed to find chats by user id'); + } + } + async findByUserIdIncludingDeleted(userId) { + const startTime = Date.now(); + try { + const result = await this.repo + .createQueryBuilder('chat') + .where(':userId = ANY(chat.users)', { userId }) + .getMany(); + (0, Logger_1.logDatabase)('Chats retrieved by user id (including deleted)', `findByUserIdIncludingDeleted(${userId})`, Date.now() - startTime, { + userId, + count: result.length + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findByUserIdIncludingDeleted error', error); + throw new Error('Failed to find all chats by user id'); + } + } + async findByGameId(gameId) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ + gameId, + type: ChatAggregate_1.ChatType.GAME, + state: ChatAggregate_1.ChatState.ACTIVE + }); + (0, Logger_1.logDatabase)('Chat retrieved by game id', `findByGameId(${gameId})`, Date.now() - startTime, { + gameId, + found: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findByGameId error', error); + throw new Error('Failed to find chat by game id'); + } + } + async findActiveChatsForUser(userId) { + const startTime = Date.now(); + try { + const result = await this.repo + .createQueryBuilder('chat') + .where(':userId = ANY(chat.users)', { userId }) + .andWhere('chat.state = :state', { state: ChatAggregate_1.ChatState.ACTIVE }) + .orderBy('chat.lastActivity', 'DESC') + .getMany(); + (0, Logger_1.logDatabase)('Active chats retrieved for user', `findActiveChatsForUser(${userId})`, Date.now() - startTime, { + userId, + count: result.length + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findActiveChatsForUser error', error); + throw new Error('Failed to find active chats for user'); + } + } + async findInactiveChats(inactivityMinutes) { + const startTime = Date.now(); + try { + const cutoffDate = new Date(Date.now() - inactivityMinutes * 60 * 1000); + const result = await this.repo + .createQueryBuilder('chat') + .where('chat.state = :state', { state: ChatAggregate_1.ChatState.ACTIVE }) + .andWhere('(chat.lastActivity < :cutoffDate OR chat.lastActivity IS NULL)', { cutoffDate }) + .getMany(); + (0, Logger_1.logDatabase)('Inactive chats retrieved', `findInactiveChats(${inactivityMinutes}min)`, Date.now() - startTime, { + inactivityMinutes, + count: result.length, + cutoffDate + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.findInactiveChats error', error); + throw new Error('Failed to find inactive chats'); + } + } + async update(id, update) { + const startTime = Date.now(); + try { + await this.repo.update(id, update); + const result = await this.findById(id); + (0, Logger_1.logDatabase)('Chat updated successfully', `update(${id})`, Date.now() - startTime, { + chatId: id, + updatedFields: Object.keys(update), + success: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.update error', error); + throw new Error('Failed to update chat in database'); + } + } + async delete(id) { + const startTime = Date.now(); + try { + const result = await this.repo.delete(id); + (0, Logger_1.logDatabase)('Chat deleted', `delete(${id})`, Date.now() - startTime, { + chatId: id, + affected: result.affected + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.delete error', error); + throw new Error('Failed to delete chat'); + } + } + async softDelete(id) { + const startTime = Date.now(); + try { + await this.repo.update(id, { state: ChatAggregate_1.ChatState.SOFT_DELETE }); + const result = await this.findById(id); + (0, Logger_1.logDatabase)('Chat soft deleted', `softDelete(${id})`, Date.now() - startTime, { + chatId: id, + success: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.softDelete error', error); + throw new Error('Failed to soft delete chat'); + } + } + async archiveChat(chat) { + const startTime = Date.now(); + try { + const archive = new ChatArchiveAggregate_1.ChatArchiveAggregate(); + archive.chatId = chat.id; + archive.archivedMessages = chat.messages; + archive.archivedAt = new Date(); + archive.chatType = chat.type; + archive.chatName = chat.name; + archive.gameId = chat.gameId; + archive.participants = chat.users; + const archivedResult = await this.archiveRepo.save(archive); + await this.repo.update(chat.id, { + state: ChatAggregate_1.ChatState.ARCHIVE, + messages: [], + archiveDate: new Date() + }); + (0, Logger_1.logDatabase)('Chat archived successfully', `archiveChat(${chat.id})`, Date.now() - startTime, { + chatId: chat.id, + messageCount: chat.messages.length, + archiveId: archivedResult.id + }); + return archivedResult; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.archiveChat error', error); + throw new Error('Failed to archive chat'); + } + } + async getArchivedChat(chatId) { + const startTime = Date.now(); + try { + const result = await this.archiveRepo.findOneBy({ chatId }); + (0, Logger_1.logDatabase)('Archived chat retrieved', `getArchivedChat(${chatId})`, Date.now() - startTime, { + chatId, + found: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.getArchivedChat error', error); + throw new Error('Failed to retrieve archived chat'); + } + } + async restoreFromArchive(chatId) { + const startTime = Date.now(); + try { + const archive = await this.archiveRepo.findOneBy({ chatId }); + if (!archive) { + return null; + } + // Game chats cannot be restored, only viewed + if (archive.chatType === ChatAggregate_1.ChatType.GAME) { + (0, Logger_1.logDatabase)('Game chat restore attempt blocked', `restoreFromArchive(${chatId})`, Date.now() - startTime, { + chatId, + chatType: archive.chatType, + blocked: true + }); + return null; + } + // Restore messages to the chat + await this.repo.update(chatId, { + state: ChatAggregate_1.ChatState.ACTIVE, + messages: archive.archivedMessages, + lastActivity: new Date(), + archiveDate: null + }); + const result = await this.findById(chatId); + (0, Logger_1.logDatabase)('Chat restored from archive', `restoreFromArchive(${chatId})`, Date.now() - startTime, { + chatId, + messageCount: archive.archivedMessages.length, + success: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('ChatRepository.restoreFromArchive error', error); + throw new Error('Failed to restore chat from archive'); + } + } +} +exports.ChatRepository = ChatRepository; +//# sourceMappingURL=ChatRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.js.map b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.js.map new file mode 100644 index 00000000..e92f51b2 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ChatRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ChatRepository.js","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/ChatRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.d.ts b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.d.ts new file mode 100644 index 00000000..56a83a7f --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.d.ts @@ -0,0 +1,23 @@ +import { ContactAggregate } from '../../Domain/Contact/ContactAggregate'; +import { IContactRepository } from '../../Domain/IRepository/IContactRepository'; +export declare class ContactRepository implements IContactRepository { + private repo; + constructor(); + create(contact: Partial): Promise & ContactAggregate>; + findById(id: string): Promise; + findByPage(from: number, to: number): Promise<{ + contacts: ContactAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + contacts: ContactAggregate[]; + totalCount: number; + }>; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + searchIncludingDeleted(searchTerm: string): Promise; + search(searchTerm: string): Promise; +} +//# sourceMappingURL=ContactRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.d.ts.map new file mode 100644 index 00000000..8a92ea69 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactRepository.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/ContactRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAgB,MAAM,uCAAuC,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AAGjF,qBAAa,iBAAkB,YAAW,kBAAkB;IACxD,OAAO,CAAC,IAAI,CAA+B;;IAMrC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAIzC,QAAQ,CAAC,EAAE,EAAE,MAAM;IAQnB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAkCnG,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA6BnH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAKpD,MAAM,CAAC,EAAE,EAAE,MAAM;IAIjB,UAAU,CAAC,EAAE,EAAE,MAAM;IAKrB,wBAAwB,CAAC,EAAE,EAAE,MAAM;IAInC,sBAAsB,CAAC,UAAU,EAAE,MAAM;IASzC,MAAM,CAAC,UAAU,EAAE,MAAM;CASlC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.js b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.js new file mode 100644 index 00000000..ebadaf2b --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.js @@ -0,0 +1,110 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContactRepository = void 0; +const typeorm_1 = require("typeorm"); +const ormconfig_1 = require("../ormconfig"); +const ContactAggregate_1 = require("../../Domain/Contact/ContactAggregate"); +const Logger_1 = require("../../Application/Services/Logger"); +class ContactRepository { + constructor() { + this.repo = ormconfig_1.AppDataSource.getRepository(ContactAggregate_1.ContactAggregate); + } + async create(contact) { + return this.repo.save(contact); + } + async findById(id) { + return this.repo + .createQueryBuilder('contact') + .where('contact.id = :id', { id }) + .andWhere('contact.state != :softDelete', { softDelete: ContactAggregate_1.ContactState.SOFT_DELETE }) + .getOne(); + } + async findByPage(from, to) { + const startTime = performance.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count({ + where: { + state: (0, typeorm_1.Not)(ContactAggregate_1.ContactState.SOFT_DELETE) + } + }); + // Get paginated results + const contacts = await this.repo + .createQueryBuilder('contact') + .where('contact.state != :softDelete', { softDelete: ContactAggregate_1.ContactState.SOFT_DELETE }) + .orderBy('contact.createDate', 'DESC') + .limit(limit) + .offset(offset) + .getMany(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Contact page query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${contacts.length}, total: ${totalCount}, from: ${from}, to: ${to}`); + return { contacts, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Contact page query failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); + (0, Logger_1.logError)('ContactRepository.findByPage error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to get contacts page from database'); + } + } + async findByPageIncludingDeleted(from, to) { + const startTime = performance.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count(); + // Get paginated results + const contacts = await this.repo + .createQueryBuilder('contact') + .orderBy('contact.createDate', 'DESC') + .limit(limit) + .offset(offset) + .getMany(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Contact page query completed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${contacts.length}, total: ${totalCount}, from: ${from}, to: ${to}`); + return { contacts, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Contact page query failed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); + (0, Logger_1.logError)('ContactRepository.findByPageIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to get contacts page from database'); + } + } + async update(id, update) { + await this.repo.update(id, update); + return this.findById(id); + } + async delete(id) { + return this.repo.delete(id); + } + async softDelete(id) { + await this.repo.update(id, { state: ContactAggregate_1.ContactState.SOFT_DELETE }); + return this.findById(id); + } + async findByIdIncludingDeleted(id) { + return this.repo.findOneBy({ id }); // Returns contact regardless of state + } + async searchIncludingDeleted(searchTerm) { + return this.repo + .createQueryBuilder('contact') + .where('contact.name ILIKE :searchTerm', { searchTerm: `%${searchTerm}%` }) + .orWhere('contact.email ILIKE :searchTerm', { searchTerm: `%${searchTerm}%` }) + .orWhere('contact.txt ILIKE :searchTerm', { searchTerm: `%${searchTerm}%` }) + .getMany(); + } + async search(searchTerm) { + return this.repo + .createQueryBuilder('contact') + .where('contact.name ILIKE :searchTerm', { searchTerm: `%${searchTerm}%` }) + .orWhere('contact.email ILIKE :searchTerm', { searchTerm: `%${searchTerm}%` }) + .orWhere('contact.txt ILIKE :searchTerm', { searchTerm: `%${searchTerm}%` }) + .andWhere('contact.state != :softDelete', { softDelete: ContactAggregate_1.ContactState.SOFT_DELETE }) + .getMany(); + } +} +exports.ContactRepository = ContactRepository; +//# sourceMappingURL=ContactRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.js.map b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.js.map new file mode 100644 index 00000000..8b887878 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/ContactRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContactRepository.js","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/ContactRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.d.ts b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.d.ts new file mode 100644 index 00000000..9bbd4605 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.d.ts @@ -0,0 +1,54 @@ +import { DeckAggregate } from '../../Domain/Deck/DeckAggregate'; +import { IDeckRepository } from '../../Domain/IRepository/IDeckRepository'; +export declare class DeckRepository implements IDeckRepository { + private repo; + constructor(); + create(deck: Partial): Promise & DeckAggregate>; + findByPage(from: number, to: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + search(query: string, limit?: number, offset?: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; + /** + * Count active (non-soft-deleted) decks for a specific user + * @param userId - User ID to count decks for + * @returns Number of active decks + */ + countActiveByUserId(userId: string): Promise; + /** + * Count organizational decks for a specific user + * @param userId - User ID to count organizational decks for + * @returns Number of organizational decks + */ + countOrganizationalByUserId(userId: string): Promise; + /** + * Find decks with filtering based on user permissions and mandatory pagination + * @param userId - User ID for filtering + * @param userOrgId - User's organization ID (if any) + * @param isAdmin - Whether user is admin (bypasses filtering) + * @param from - Start index for pagination (default: 0) + * @param to - End index for pagination (default: 49) + * @returns Paginated filtered list of decks with total count + */ + findFilteredDecks(userId: string, userOrgId?: string | null, isAdmin?: boolean, from?: number, to?: number): Promise<{ + decks: DeckAggregate[]; + totalCount: number; + }>; +} +//# sourceMappingURL=DeckRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.d.ts.map new file mode 100644 index 00000000..e9a42cea --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckRepository.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/DeckRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAgB,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAI3E,qBAAa,cAAe,YAAW,eAAe;IAClD,OAAO,CAAC,IAAI,CAA4B;;IAKlC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC;IAInC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA+B7F,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B7G,QAAQ,CAAC,EAAE,EAAE,MAAM;IASnB,wBAAwB,CAAC,EAAE,EAAE,MAAM;IAInC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC;IAKjD,MAAM,CAAC,EAAE,EAAE,MAAM;IAIjB,UAAU,CAAC,EAAE,EAAE,MAAM;IAKrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA6BtH,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B5I;;;;OAIG;IACG,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB1D;;;;OAIG;IACG,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBlE;;;;;;;;OAQG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,GAAE,MAAU,EAAE,EAAE,GAAE,MAAW,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAkFpL"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.js b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.js new file mode 100644 index 00000000..485c5e06 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.js @@ -0,0 +1,264 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeckRepository = void 0; +const typeorm_1 = require("typeorm"); +const ormconfig_1 = require("../ormconfig"); +const DeckAggregate_1 = require("../../Domain/Deck/DeckAggregate"); +const Logger_1 = require("../../Application/Services/Logger"); +const AdminBypassService_1 = require("../../Application/Services/AdminBypassService"); +class DeckRepository { + constructor() { + this.repo = ormconfig_1.AppDataSource.getRepository(DeckAggregate_1.DeckAggregate); + } + async create(deck) { + return this.repo.save(deck); + } + async findByPage(from, to) { + const startTime = performance.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count({ + where: { state: (0, typeorm_1.Not)(DeckAggregate_1.State.SOFT_DELETE) } + }); + // Get paginated results + const decks = await this.repo.find({ + where: { state: (0, typeorm_1.Not)(DeckAggregate_1.State.SOFT_DELETE) }, + order: { updatedate: 'DESC' }, + take: limit, + skip: offset + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck page query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${decks.length}, total: ${totalCount}, from: ${from}, to: ${to}`); + return { decks, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck page query failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); + (0, Logger_1.logError)('DeckRepository.findByPage error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to get decks page from database'); + } + } + async findByPageIncludingDeleted(from, to) { + const startTime = performance.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count(); + // Get paginated results + const decks = await this.repo.find({ + order: { updatedate: 'DESC' }, + take: limit, + skip: offset + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck page query completed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${decks.length}, total: ${totalCount}, from: ${from}, to: ${to}`); + return { decks, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck page query failed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); + (0, Logger_1.logError)('DeckRepository.findByPageIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to get decks page from database'); + } + } + async findById(id) { + return this.repo.findOne({ + where: { + id, + state: (0, typeorm_1.Not)(DeckAggregate_1.State.SOFT_DELETE) + } + }); + } + async findByIdIncludingDeleted(id) { + return this.repo.findOneBy({ id }); + } + async update(id, update) { + await this.repo.update(id, update); + return this.findById(id); + } + async delete(id) { + return this.repo.delete(id); + } + async softDelete(id) { + await this.repo.update(id, { state: DeckAggregate_1.State.SOFT_DELETE }); + return this.findById(id); + } + async search(query, limit = 20, offset = 0) { + const startTime = performance.now(); + try { + const searchPattern = `%${query.toLowerCase()}%`; + const queryBuilder = this.repo.createQueryBuilder('deck') + .where('deck.state != :softDelete', { softDelete: DeckAggregate_1.State.SOFT_DELETE }) + .andWhere('LOWER(deck.name) LIKE :pattern', { pattern: searchPattern }); + const totalCount = await queryBuilder.getCount(); + const decks = await queryBuilder + .orderBy('deck.name', 'ASC') + .limit(limit) + .offset(offset) + .getMany(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck search completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${decks.length}, total: ${totalCount}, searchTerm: "${query}", limit: ${limit}, offset: ${offset}`); + return { decks, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck search failed', `executionTime: ${Math.round(endTime - startTime)}ms, searchTerm: "${query}"`); + (0, Logger_1.logError)('DeckRepository.search error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to search decks in database'); + } + } + async searchIncludingDeleted(query, limit = 20, offset = 0) { + const startTime = performance.now(); + try { + const searchPattern = `%${query.toLowerCase()}%`; + const queryBuilder = this.repo.createQueryBuilder('deck') + .where('LOWER(deck.name) LIKE :pattern', { pattern: searchPattern }); + const totalCount = await queryBuilder.getCount(); + const decks = await queryBuilder + .orderBy('deck.name', 'ASC') + .limit(limit) + .offset(offset) + .getMany(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck search completed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${decks.length}, total: ${totalCount}, searchTerm: "${query}", limit: ${limit}, offset: ${offset}`); + return { decks, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Deck search failed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, searchTerm: "${query}"`); + (0, Logger_1.logError)('DeckRepository.searchIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to search all decks in database'); + } + } + /** + * Count active (non-soft-deleted) decks for a specific user + * @param userId - User ID to count decks for + * @returns Number of active decks + */ + async countActiveByUserId(userId) { + const startTime = performance.now(); + try { + const count = await this.repo.count({ + where: { + userid: userId, + state: (0, typeorm_1.Not)(DeckAggregate_1.State.SOFT_DELETE) + } + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('User active deck count completed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}, count: ${count}`); + return count; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('User active deck count failed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}`); + (0, Logger_1.logError)('DeckRepository.countActiveByUserId error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to count active decks for user'); + } + } + /** + * Count organizational decks for a specific user + * @param userId - User ID to count organizational decks for + * @returns Number of organizational decks + */ + async countOrganizationalByUserId(userId) { + const startTime = performance.now(); + try { + const count = await this.repo.count({ + where: { + userid: userId, + ctype: DeckAggregate_1.CType.ORGANIZATION, + state: (0, typeorm_1.Not)(DeckAggregate_1.State.SOFT_DELETE) + } + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('User organizational deck count completed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}, count: ${count}`); + return count; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('User organizational deck count failed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}`); + (0, Logger_1.logError)('DeckRepository.countOrganizationalByUserId error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to count organizational decks for user'); + } + } + /** + * Find decks with filtering based on user permissions and mandatory pagination + * @param userId - User ID for filtering + * @param userOrgId - User's organization ID (if any) + * @param isAdmin - Whether user is admin (bypasses filtering) + * @param from - Start index for pagination (default: 0) + * @param to - End index for pagination (default: 49) + * @returns Paginated filtered list of decks with total count + */ + async findFilteredDecks(userId, userOrgId, isAdmin, from = 0, to = 49) { + const startTime = performance.now(); + try { + // Validate pagination parameters + if (from < 0 || to < from) { + throw new Error('Invalid pagination parameters'); + } + const limit = to - from + 1; + if (limit > 100) { + throw new Error('Page size too large. Maximum 100 records per request'); + } + const skip = from; + const take = limit; + // Admin gets ALL decks with pagination + if (isAdmin) { + AdminBypassService_1.AdminBypassService.logAdminBypass('FIND_FILTERED_DECKS_BYPASS', userId, 'all-decks-filtered', { + bypassType: 'admin-all-decks-filtered', + userOrgId, + from, + to, + operation: 'read' + }); + const [decks, totalCount] = await this.repo.findAndCount({ + where: { state: (0, typeorm_1.Not)(DeckAggregate_1.State.SOFT_DELETE) }, + relations: ['organization'], + order: { creationdate: 'DESC' }, + skip, + take + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Admin filtered deck query completed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}, found: ${decks.length}, totalCount: ${totalCount}, isAdmin: true`); + return { decks, totalCount }; + } + // Regular user complex filtering + const queryBuilder = this.repo.createQueryBuilder('deck') + .leftJoinAndSelect('deck.organization', 'org') + .where('deck.state != :deletedState', { deletedState: DeckAggregate_1.State.SOFT_DELETE }); + queryBuilder.andWhere('(' + + // User's private decks + '(deck.userid = :userId AND deck.ctype = :privateType) OR ' + + // All public decks + '(deck.ctype = :publicType)' + + // Organization decks from same org (if user has org) + (userOrgId ? ' OR (deck.ctype = :orgType AND org.id = :orgId)' : '') + + ')', { + userId, + privateType: DeckAggregate_1.CType.PRIVATE, + publicType: DeckAggregate_1.CType.PUBLIC, + ...(userOrgId && { orgType: DeckAggregate_1.CType.ORGANIZATION, orgId: userOrgId }) + }); + queryBuilder + .orderBy('deck.creationdate', 'DESC') + .skip(skip) + .take(take); + const [decks, totalCount] = await queryBuilder.getManyAndCount(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('User filtered deck query completed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}, userOrgId: ${userOrgId}, found: ${decks.length}, totalCount: ${totalCount}, isAdmin: false`); + return { decks, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Filtered deck query failed', `executionTime: ${Math.round(endTime - startTime)}ms, userId: ${userId}, isAdmin: ${isAdmin}`); + (0, Logger_1.logError)('DeckRepository.findFilteredDecks error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to find filtered decks'); + } + } +} +exports.DeckRepository = DeckRepository; +//# sourceMappingURL=DeckRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.js.map b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.js.map new file mode 100644 index 00000000..d17a0a4f --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/DeckRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DeckRepository.js","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/DeckRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.d.ts b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.d.ts new file mode 100644 index 00000000..7d3c6e6d --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.d.ts @@ -0,0 +1,29 @@ +import { OrganizationAggregate } from '../../Domain/Organization/OrganizationAggregate'; +import { IOrganizationRepository } from '../../Domain/IRepository/IOrganizationRepository'; +export declare class OrganizationRepository implements IOrganizationRepository { + private repo; + constructor(); + create(org: Partial): Promise & OrganizationAggregate>; + findByPage(from: number, to: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + search(query: string, limit?: number, offset?: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; + searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ + organizations: OrganizationAggregate[]; + totalCount: number; + }>; +} +//# sourceMappingURL=OrganizationRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.d.ts.map new file mode 100644 index 00000000..4d6ce71d --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationRepository.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/OrganizationRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAqB,MAAM,iDAAiD,CAAC;AAC3G,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAG3F,qBAAa,sBAAuB,YAAW,uBAAuB;IAClE,OAAO,CAAC,IAAI,CAAoC;;IAK1C,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAI1C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA+B7G,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B7H,QAAQ,CAAC,EAAE,EAAE,MAAM;IASnB,wBAAwB,CAAC,EAAE,EAAE,MAAM;IAInC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAKzD,MAAM,CAAC,EAAE,EAAE,MAAM;IAIjB,UAAU,CAAC,EAAE,EAAE,MAAM;IAKrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA6BtI,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,qBAAqB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAgC/J"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.js b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.js new file mode 100644 index 00000000..a6b35663 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.js @@ -0,0 +1,141 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OrganizationRepository = void 0; +const typeorm_1 = require("typeorm"); +const ormconfig_1 = require("../ormconfig"); +const OrganizationAggregate_1 = require("../../Domain/Organization/OrganizationAggregate"); +const Logger_1 = require("../../Application/Services/Logger"); +class OrganizationRepository { + constructor() { + this.repo = ormconfig_1.AppDataSource.getRepository(OrganizationAggregate_1.OrganizationAggregate); + } + async create(org) { + return this.repo.save(org); + } + async findByPage(from, to) { + const startTime = performance.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count({ + where: { state: (0, typeorm_1.Not)(OrganizationAggregate_1.OrganizationState.SOFT_DELETE) } + }); + // Get paginated results + const organizations = await this.repo.find({ + where: { state: (0, typeorm_1.Not)(OrganizationAggregate_1.OrganizationState.SOFT_DELETE) }, + order: { name: 'ASC' }, + take: limit, + skip: offset + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization page query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${organizations.length}, total: ${totalCount}, from: ${from}, to: ${to}`); + return { organizations, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization page query failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); + (0, Logger_1.logError)('OrganizationRepository.findByPage error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to get organizations page from database'); + } + } + async findByPageIncludingDeleted(from, to) { + const startTime = performance.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count(); + // Get paginated results + const organizations = await this.repo.find({ + order: { name: 'ASC' }, + take: limit, + skip: offset + }); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization page query completed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${organizations.length}, total: ${totalCount}, from: ${from}, to: ${to}`); + return { organizations, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization page query failed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`); + (0, Logger_1.logError)('OrganizationRepository.findByPageIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to get organizations page from database'); + } + } + async findById(id) { + return this.repo.findOne({ + where: { + id, + state: (0, typeorm_1.Not)(OrganizationAggregate_1.OrganizationState.SOFT_DELETE) + } + }); + } + async findByIdIncludingDeleted(id) { + return this.repo.findOneBy({ id }); + } + async update(id, update) { + await this.repo.update(id, update); + return this.findById(id); + } + async delete(id) { + return this.repo.delete(id); + } + async softDelete(id) { + await this.repo.update(id, { state: OrganizationAggregate_1.OrganizationState.SOFT_DELETE }); + return this.findById(id); + } + async search(query, limit = 20, offset = 0) { + const startTime = performance.now(); + try { + const searchPattern = `%${query.toLowerCase()}%`; + const queryBuilder = this.repo.createQueryBuilder('org') + .where('org.state != :softDelete', { softDelete: OrganizationAggregate_1.OrganizationState.SOFT_DELETE }) + .andWhere('(LOWER(org.name) LIKE :pattern OR LOWER(org.contactfname) LIKE :pattern OR LOWER(org.contactlname) LIKE :pattern OR LOWER(org.contactemail) LIKE :pattern OR LOWER(CONCAT(org.contactfname, \' \', org.contactlname)) LIKE :pattern)', { pattern: searchPattern }); + const totalCount = await queryBuilder.getCount(); + const organizations = await queryBuilder + .orderBy('org.name', 'ASC') + .limit(limit) + .offset(offset) + .getMany(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization search completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${organizations.length}, total: ${totalCount}, searchTerm: "${query}", limit: ${limit}, offset: ${offset}`); + return { organizations, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization search failed', `executionTime: ${Math.round(endTime - startTime)}ms, searchTerm: "${query}"`); + (0, Logger_1.logError)('OrganizationRepository.search error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to search organizations in database'); + } + } + async searchIncludingDeleted(query, limit = 20, offset = 0) { + const startTime = performance.now(); + try { + const searchPattern = `%${query.toLowerCase()}%`; + const queryBuilder = this.repo.createQueryBuilder('org') + .where('LOWER(org.name) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(org.contactfname) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(org.contactlname) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(org.contactemail) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(CONCAT(org.contactfname, \' \', org.contactlname)) LIKE :pattern', { pattern: searchPattern }); + const totalCount = await queryBuilder.getCount(); + const organizations = await queryBuilder + .orderBy('org.name', 'ASC') + .limit(limit) + .offset(offset) + .getMany(); + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization search completed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${organizations.length}, total: ${totalCount}, searchTerm: "${query}", limit: ${limit}, offset: ${offset}`); + return { organizations, totalCount }; + } + catch (error) { + const endTime = performance.now(); + (0, Logger_1.logDatabase)('Organization search failed (including deleted)', `executionTime: ${Math.round(endTime - startTime)}ms, searchTerm: "${query}"`); + (0, Logger_1.logError)('OrganizationRepository.searchIncludingDeleted error', error instanceof Error ? error : new Error(String(error))); + throw new Error('Failed to search all organizations in database'); + } + } +} +exports.OrganizationRepository = OrganizationRepository; +//# sourceMappingURL=OrganizationRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.js.map b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.js.map new file mode 100644 index 00000000..afef42a8 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/OrganizationRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OrganizationRepository.js","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/OrganizationRepository.ts"],"names":[],"mappings":";;;AAAA,qCAA0C;AAC1C,4CAA6C;AAC7C,2FAA2G;AAE3G,8DAA0E;AAE1E,MAAa,sBAAsB;IAE/B;QACI,IAAI,CAAC,IAAI,GAAG,yBAAa,CAAC,aAAa,CAAC,6CAAqB,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAmC;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,EAAU;QACrC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC;YAEpB,iCAAiC;YACjC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;gBACrC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,aAAG,EAAC,yCAAiB,CAAC,WAAW,CAAC,EAAE;aACvD,CAAC,CAAC;YAEH,wBAAwB;YACxB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAA,aAAG,EAAC,yCAAiB,CAAC,WAAW,CAAC,EAAE;gBACpD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;gBACtB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,mCAAmC,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,cAAc,aAAa,CAAC,MAAM,YAAY,UAAU,WAAW,IAAI,SAAS,EAAE,EAAE,CAAC,CAAC;YAExL,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,gCAAgC,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,aAAa,IAAI,SAAS,EAAE,EAAE,CAAC,CAAC;YAC/H,IAAA,iBAAQ,EAAC,yCAAyC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC/G,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IAED,KAAK,CAAC,0BAA0B,CAAC,IAAY,EAAE,EAAU;QACrD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC;YAEpB,iCAAiC;YACjC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAE3C,wBAAwB;YACxB,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;gBACtB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,uDAAuD,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,cAAc,aAAa,CAAC,MAAM,YAAY,UAAU,WAAW,IAAI,SAAS,EAAE,EAAE,CAAC,CAAC;YAE5M,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,oDAAoD,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,aAAa,IAAI,SAAS,EAAE,EAAE,CAAC,CAAC;YACnJ,IAAA,iBAAQ,EAAC,yDAAyD,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC/H,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YACrB,KAAK,EAAE;gBACH,EAAE;gBACF,KAAK,EAAE,IAAA,aAAG,EAAC,yCAAiB,CAAC,WAAW,CAAC;aAC5C;SACJ,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,EAAU;QACrC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,MAAsC;QAC3D,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QACvB,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,yCAAiB,CAAC,WAAW,EAAE,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE,EAAE,SAAiB,CAAC;QAC9D,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;YAEjD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;iBACnD,KAAK,CAAC,0BAA0B,EAAE,EAAE,UAAU,EAAE,yCAAiB,CAAC,WAAW,EAAE,CAAC;iBAChF,QAAQ,CAAC,sOAAsO,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;YAElR,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,CAAC;YAEjD,MAAM,aAAa,GAAG,MAAM,YAAY;iBACnC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;iBAC1B,KAAK,CAAC,KAAK,CAAC;iBACZ,MAAM,CAAC,MAAM,CAAC;iBACd,OAAO,EAAE,CAAC;YAEf,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,+BAA+B,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,cAAc,aAAa,CAAC,MAAM,YAAY,UAAU,kBAAkB,KAAK,aAAa,KAAK,aAAa,MAAM,EAAE,CAAC,CAAC;YAEtN,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,4BAA4B,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,oBAAoB,KAAK,GAAG,CAAC,CAAC;YACzH,IAAA,iBAAQ,EAAC,qCAAqC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3G,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAClE,CAAC;IACL,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,KAAa,EAAE,QAAgB,EAAE,EAAE,SAAiB,CAAC;QAC9E,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;YAEjD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;iBACnD,KAAK,CAAC,+BAA+B,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;iBAClE,OAAO,CAAC,uCAAuC,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;iBAC5E,OAAO,CAAC,uCAAuC,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;iBAC5E,OAAO,CAAC,uCAAuC,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;iBAC5E,OAAO,CAAC,wEAAwE,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;YAEnH,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,CAAC;YAEjD,MAAM,aAAa,GAAG,MAAM,YAAY;iBACnC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;iBAC1B,KAAK,CAAC,KAAK,CAAC;iBACZ,MAAM,CAAC,MAAM,CAAC;iBACd,OAAO,EAAE,CAAC;YAEf,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,mDAAmD,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,cAAc,aAAa,CAAC,MAAM,YAAY,UAAU,kBAAkB,KAAK,aAAa,KAAK,aAAa,MAAM,EAAE,CAAC,CAAC;YAE1O,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAA,oBAAW,EAAC,gDAAgD,EAAE,kBAAkB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,oBAAoB,KAAK,GAAG,CAAC,CAAC;YAC7I,IAAA,iBAAQ,EAAC,qDAAqD,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3H,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;CAEJ;AA7JD,wDA6JC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.d.ts b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.d.ts new file mode 100644 index 00000000..029a4b6f --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.d.ts @@ -0,0 +1,33 @@ +import { UserAggregate } from '../../Domain/User/UserAggregate'; +import { IUserRepository } from '../../Domain/IRepository/IUserRepository'; +export declare class UserRepository implements IUserRepository { + private repo; + constructor(); + create(user: Partial): Promise & UserAggregate>; + findByPage(from: number, to: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + findByPageIncludingDeleted(from: number, to: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + findById(id: string): Promise; + findByIdIncludingDeleted(id: string): Promise; + findByUsername(username: string): Promise; + findByEmail(email: string): Promise; + findByToken(token: string): Promise; + update(id: string, update: Partial): Promise; + delete(id: string): Promise; + softDelete(id: string): Promise; + deactivate(id: string): Promise; + search(query: string, limit?: number, offset?: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; + searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ + users: UserAggregate[]; + totalCount: number; + }>; +} +//# sourceMappingURL=UserRepository.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.d.ts.map new file mode 100644 index 00000000..1d34747e --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UserRepository.d.ts","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/UserRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAa,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAG3E,qBAAa,cAAe,YAAW,eAAe;IAClD,OAAO,CAAC,IAAI,CAA4B;;IAKlC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC;IAsBnC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA+B7F,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B7G,QAAQ,CAAC,EAAE,EAAE,MAAM;IAyBnB,wBAAwB,CAAC,EAAE,EAAE,MAAM;IAoBnC,cAAc,CAAC,QAAQ,EAAE,MAAM;IAe/B,WAAW,CAAC,KAAK,EAAE,MAAM;IAezB,WAAW,CAAC,KAAK,EAAE,MAAM;IAezB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC;IA4BjD,MAAM,CAAC,EAAE,EAAE,MAAM;IAqBjB,UAAU,CAAC,EAAE,EAAE,MAAM;IAsBrB,UAAU,CAAC,EAAE,EAAE,MAAM;IAsBrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAkCtH,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,EAAE,MAAM,GAAE,MAAU,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAsC/I"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.js b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.js new file mode 100644 index 00000000..78e34135 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.js @@ -0,0 +1,312 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserRepository = void 0; +const typeorm_1 = require("typeorm"); +const ormconfig_1 = require("../ormconfig"); +const UserAggregate_1 = require("../../Domain/User/UserAggregate"); +const Logger_1 = require("../../Application/Services/Logger"); +class UserRepository { + constructor() { + this.repo = ormconfig_1.AppDataSource.getRepository(UserAggregate_1.UserAggregate); + } + async create(user) { + const startTime = Date.now(); + try { + const result = await this.repo.save(user); + (0, Logger_1.logDatabase)('User created successfully', undefined, Date.now() - startTime, { + userId: result.id, + username: user.username, + email: user.email + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.create error', error); + // Handle unique constraint violations + if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique'))) { + throw new Error('User with this username or email already exists'); + } + throw new Error('Failed to create user in database'); + } + } + async findByPage(from, to) { + const startTime = Date.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count({ + where: { state: (0, typeorm_1.Not)(UserAggregate_1.UserState.SOFT_DELETE) } + }); + // Get paginated results + const users = await this.repo.find({ + where: { state: (0, typeorm_1.Not)(UserAggregate_1.UserState.SOFT_DELETE) }, + order: { regdate: 'DESC' }, + take: limit, + skip: offset + }); + (0, Logger_1.logDatabase)('User page query completed', `from: ${from}, to: ${to}`, Date.now() - startTime, { + found: users.length, + total: totalCount + }); + return { users, totalCount }; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findByPage error', error); + throw new Error('Failed to get users page from database'); + } + } + async findByPageIncludingDeleted(from, to) { + const startTime = Date.now(); + try { + const limit = to - from + 1; + const offset = from; + // Get total count for pagination + const totalCount = await this.repo.count(); + // Get paginated results + const users = await this.repo.find({ + order: { regdate: 'DESC' }, + take: limit, + skip: offset + }); + (0, Logger_1.logDatabase)('User page query completed (including deleted)', `from: ${from}, to: ${to}`, Date.now() - startTime, { + found: users.length, + total: totalCount + }); + return { users, totalCount }; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findByPageIncludingDeleted error', error); + throw new Error('Failed to get users page from database'); + } + } + async findById(id) { + const startTime = Date.now(); + try { + const result = await this.repo.findOne({ + where: { + id, + state: (0, typeorm_1.Not)(UserAggregate_1.UserState.SOFT_DELETE) + } + }); + (0, Logger_1.logDatabase)('User findById query completed', `findOneBy({ id: ${id} })`, Date.now() - startTime, { + found: !!result, + userId: id + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findById error', error); + if (error instanceof Error && error.message.includes('invalid input syntax for type uuid')) { + return null; + } + throw new Error('Failed to retrieve user from database'); + } + } + async findByIdIncludingDeleted(id) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ id }); + (0, Logger_1.logDatabase)('User findByIdIncludingDeleted query completed', `findOneBy({ id: ${id} })`, Date.now() - startTime, { + found: !!result, + userId: id + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findByIdIncludingDeleted error', error); + if (error instanceof Error && error.message.includes('invalid input syntax for type uuid')) { + return null; + } + throw new Error('Failed to retrieve user from database'); + } + } + async findByUsername(username) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ username }); + (0, Logger_1.logDatabase)('User findByUsername query completed', `findOneBy({ username: ${username} })`, Date.now() - startTime, { + found: !!result, + username + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findByUsername error', error); + throw new Error('Failed to retrieve user by username from database'); + } + } + async findByEmail(email) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ email }); + (0, Logger_1.logDatabase)('User findByEmail query completed', `findOneBy({ email: ${email} })`, Date.now() - startTime, { + found: !!result, + email + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findByEmail error', error); + throw new Error('Failed to retrieve user by email from database'); + } + } + async findByToken(token) { + const startTime = Date.now(); + try { + const result = await this.repo.findOneBy({ token: token }); + (0, Logger_1.logDatabase)('User findByToken query completed', `findOneBy({ token })`, Date.now() - startTime, { + found: !!result, + tokenPrefix: token.substring(0, 8) + '...' + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.findByToken error', error); + throw new Error('Failed to retrieve user by token from database'); + } + } + async update(id, update) { + const startTime = Date.now(); + try { + await this.repo.update(id, update); + const result = await this.findById(id); + (0, Logger_1.logDatabase)('User updated successfully', `update(${id})`, Date.now() - startTime, { + userId: id, + updatedFields: Object.keys(update), + success: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.update error', error); + // Handle unique constraint violations + if (error instanceof Error && (error.message.includes('duplicate') || error.message.includes('unique'))) { + throw new Error('Username or email already exists'); + } + // Handle invalid UUID format + if (error instanceof Error && error.message.includes('invalid input syntax for type uuid')) { + throw new Error('Invalid user ID format'); + } + throw new Error('Failed to update user in database'); + } + } + async delete(id) { + const startTime = Date.now(); + try { + const result = await this.repo.delete(id); + (0, Logger_1.logDatabase)('User deleted successfully', `delete(${id})`, Date.now() - startTime, { + userId: id, + affected: result.affected + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.delete error', error); + // Handle invalid UUID format + if (error instanceof Error && error.message.includes('invalid input syntax for type uuid')) { + throw new Error('Invalid user ID format'); + } + throw new Error('Failed to delete user from database'); + } + } + async softDelete(id) { + const startTime = Date.now(); + try { + await this.repo.update(id, { state: UserAggregate_1.UserState.SOFT_DELETE }); + const result = await this.findById(id); + (0, Logger_1.logDatabase)('User soft deleted successfully', `update(${id}, { state: SOFT_DELETE })`, Date.now() - startTime, { + userId: id, + success: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.softDelete error', error); + // Handle invalid UUID format + if (error instanceof Error && error.message.includes('invalid input syntax for type uuid')) { + throw new Error('Invalid user ID format'); + } + throw new Error('Failed to soft delete user in database'); + } + } + async deactivate(id) { + const startTime = Date.now(); + try { + await this.repo.update(id, { state: UserAggregate_1.UserState.DEACTIVATED }); + const result = await this.findById(id); + (0, Logger_1.logDatabase)('User deactivated successfully', `update(${id}, { state: DEACTIVATED })`, Date.now() - startTime, { + userId: id, + success: !!result + }); + return result; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.deactivate error', error); + // Handle invalid UUID format + if (error instanceof Error && error.message.includes('invalid input syntax for type uuid')) { + throw new Error('Invalid user ID format'); + } + throw new Error('Failed to deactivate user in database'); + } + } + async search(query, limit = 20, offset = 0) { + const startTime = Date.now(); + try { + const searchPattern = `%${query.toLowerCase()}%`; + const queryBuilder = this.repo.createQueryBuilder('user') + .where('user.state != :softDelete', { softDelete: UserAggregate_1.UserState.SOFT_DELETE }) + .andWhere('(LOWER(user.username) LIKE :pattern OR LOWER(user.email) LIKE :pattern OR LOWER(user.fname) LIKE :pattern OR LOWER(user.lname) LIKE :pattern OR LOWER(CONCAT(user.fname, \' \', user.lname)) LIKE :pattern)', { pattern: searchPattern }); + const totalCount = await queryBuilder.getCount(); + const users = await queryBuilder + .orderBy('user.username', 'ASC') + .limit(limit) + .offset(offset) + .getMany(); + (0, Logger_1.logDatabase)('User search completed', `search query: ${query.substring(0, 50)}...`, Date.now() - startTime, { + query, + limit, + offset, + totalCount, + returnedCount: users.length + }); + return { users, totalCount }; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.search error', error); + throw new Error('Failed to search users in database'); + } + } + async searchIncludingDeleted(query, limit = 20, offset = 0) { + const startTime = Date.now(); + try { + const searchPattern = `%${query.toLowerCase()}%`; + const queryBuilder = this.repo.createQueryBuilder('user') + .where('LOWER(user.username) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(user.email) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(user.fname) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(user.lname) LIKE :pattern', { pattern: searchPattern }) + .orWhere('LOWER(CONCAT(user.fname, \' \', user.lname)) LIKE :pattern', { pattern: searchPattern }); + const totalCount = await queryBuilder.getCount(); + const users = await queryBuilder + .orderBy('user.username', 'ASC') + .limit(limit) + .offset(offset) + .getMany(); + (0, Logger_1.logDatabase)('User search completed (including deleted)', `search query: ${query.substring(0, 50)}...`, Date.now() - startTime, { + query, + limit, + offset, + totalCount, + returnedCount: users.length + }); + return { users, totalCount }; + } + catch (error) { + (0, Logger_1.logError)('UserRepository.searchIncludingDeleted error', error); + throw new Error('Failed to search all users in database'); + } + } +} +exports.UserRepository = UserRepository; +//# sourceMappingURL=UserRepository.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.js.map b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.js.map new file mode 100644 index 00000000..83e8bbf4 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/Repository/UserRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"UserRepository.js","sourceRoot":"","sources":["../../../src/Infrastructure/Repository/UserRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/ormconfig.d.ts b/SerpentRace_Backend/dist/Infrastructure/ormconfig.d.ts new file mode 100644 index 00000000..7c7f09c4 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/ormconfig.d.ts @@ -0,0 +1,3 @@ +import { DataSource } from 'typeorm'; +export declare const AppDataSource: DataSource; +//# sourceMappingURL=ormconfig.d.ts.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/ormconfig.d.ts.map b/SerpentRace_Backend/dist/Infrastructure/ormconfig.d.ts.map new file mode 100644 index 00000000..45726959 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/ormconfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ormconfig.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/ormconfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGrC,eAAO,MAAM,aAAa,YAaxB,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/ormconfig.js b/SerpentRace_Backend/dist/Infrastructure/ormconfig.js new file mode 100644 index 00000000..0d6d1378 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/ormconfig.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AppDataSource = void 0; +const typeorm_1 = require("typeorm"); +const path_1 = require("path"); +exports.AppDataSource = new typeorm_1.DataSource({ + type: 'postgres', + host: process.env.DB_HOST || 'localhost', + port: parseInt(process.env.DB_PORT || '5432'), + username: process.env.DB_USERNAME || 'postgres', + password: process.env.DB_PASSWORD || 'password', + database: process.env.DB_NAME || 'serpentrace', + synchronize: false, // Set to false when using migrations + logging: process.env.NODE_ENV === 'development', + entities: [(0, path_1.join)(__dirname, '../Domain/**/*Aggregate.ts')], + migrations: [(0, path_1.join)(__dirname, './Migrations/*.ts')], + migrationsTableName: 'migrations', + migrationsRun: false // Let migrations run manually +}); +//# sourceMappingURL=ormconfig.js.map \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Infrastructure/ormconfig.js.map b/SerpentRace_Backend/dist/Infrastructure/ormconfig.js.map new file mode 100644 index 00000000..e07ae8a5 --- /dev/null +++ b/SerpentRace_Backend/dist/Infrastructure/ormconfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ormconfig.js","sourceRoot":"","sources":["../../src/Infrastructure/ormconfig.ts"],"names":[],"mappings":";;;AAAA,qCAAqC;AACrC,+BAA4B;AAEf,QAAA,aAAa,GAAG,IAAI,oBAAU,CAAC;IACxC,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,WAAW;IACxC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC;IAC7C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,UAAU;IAC/C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,UAAU;IAC/C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,aAAa;IAC9C,WAAW,EAAE,KAAK,EAAE,qCAAqC;IACzD,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC/C,QAAQ,EAAE,CAAC,IAAA,WAAI,EAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;IACzD,UAAU,EAAE,CAAC,IAAA,WAAI,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAClD,mBAAmB,EAAE,YAAY;IACjC,aAAa,EAAE,KAAK,CAAC,8BAA8B;CACtD,CAAC,CAAC"} \ No newline at end of file diff --git a/SerpentRace_Backend/dist/Templates/contact-response-de.html b/SerpentRace_Backend/dist/Templates/contact-response-de.html new file mode 100644 index 00000000..bb5c8d03 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/contact-response-de.html @@ -0,0 +1,137 @@ + + + + + + {{companyName}} - Antwort auf Ihre {{contactTypeString}} + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/contact-response-hu.html b/SerpentRace_Backend/dist/Templates/contact-response-hu.html new file mode 100644 index 00000000..aeac09c3 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/contact-response-hu.html @@ -0,0 +1,137 @@ + + + + + + {{companyName}} - Válasz az Ön {{contactTypeString}} üzenetére + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/contact-response.html b/SerpentRace_Backend/dist/Templates/contact-response.html new file mode 100644 index 00000000..4c928510 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/contact-response.html @@ -0,0 +1,137 @@ + + + + + + {{companyName}} - Response to Your {{contactTypeString}} + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/password-reset-de.html b/SerpentRace_Backend/dist/Templates/password-reset-de.html new file mode 100644 index 00000000..cba82184 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/password-reset-de.html @@ -0,0 +1,203 @@ + + + + + + SerpentRace - Passwort zurücksetzen + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/password-reset-hu.html b/SerpentRace_Backend/dist/Templates/password-reset-hu.html new file mode 100644 index 00000000..5ac57bbd --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/password-reset-hu.html @@ -0,0 +1,203 @@ + + + + + + SerpentRace - Jelszó visszaállítás kérése + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/password-reset.html b/SerpentRace_Backend/dist/Templates/password-reset.html new file mode 100644 index 00000000..9fbf46e4 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/password-reset.html @@ -0,0 +1,203 @@ + + + + + + SerpentRace - Password Reset Request + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/verification-de.html b/SerpentRace_Backend/dist/Templates/verification-de.html new file mode 100644 index 00000000..068c35b0 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/verification-de.html @@ -0,0 +1,186 @@ + + + + + + SerpentRace - Konto verifizieren + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/verification-hu.html b/SerpentRace_Backend/dist/Templates/verification-hu.html new file mode 100644 index 00000000..ec8e6dc4 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/verification-hu.html @@ -0,0 +1,186 @@ + + + + + + SerpentRace - Fiók megerősítése + + + + + + diff --git a/SerpentRace_Backend/dist/Templates/verification.html b/SerpentRace_Backend/dist/Templates/verification.html new file mode 100644 index 00000000..24dd5bc8 --- /dev/null +++ b/SerpentRace_Backend/dist/Templates/verification.html @@ -0,0 +1,186 @@ + + + + + + SerpentRace - Verify Your Account + + + + + + diff --git a/SerpentRace_Backend/jest.config.js b/SerpentRace_Backend/jest.config.js new file mode 100644 index 00000000..929a0400 --- /dev/null +++ b/SerpentRace_Backend/jest.config.js @@ -0,0 +1,28 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/tests', '/src'], + testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], + transform: { + '^.+\\.ts$': 'ts-jest', + }, + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/**/*.d.ts', + '!src/Api/index.ts', + '!src/Infrastructure/ormconfig.ts', + '!src/search-demo.ts' + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html'], + moduleFileExtensions: ['ts', 'js', 'json'], + setupFilesAfterEnv: ['/tests/setup.ts'], + testTimeout: 10000, + setupFiles: ['/tests/jest.setup.ts'], + verbose: true, + moduleNameMapper: { + '^@/(.*)$': '/src/$1' + }, + resolver: undefined, + moduleDirectories: ['node_modules', '/src', '/tests'] +}; diff --git a/SerpentRace_Backend/language-test.js b/SerpentRace_Backend/language-test.js new file mode 100644 index 00000000..4895e213 --- /dev/null +++ b/SerpentRace_Backend/language-test.js @@ -0,0 +1,29 @@ +// Quick test to demonstrate the language detection functionality +const { extractLanguageFromAcceptHeader } = require('./src/Api/contactRouter.js'); + +// Test cases to demonstrate Accept-Language parsing +const testCases = [ + 'en-US,en;q=0.9', + 'hu,en;q=0.9', + 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7', + 'hu-HU,hu;q=0.9,en-US;q=0.8', + 'fr-FR,fr;q=0.9,en;q=0.8', + 'es,en-US;q=0.9,en;q=0.8', + 'invalid-header', + '' +]; + +console.log('Testing Accept-Language header parsing:\n'); + +testCases.forEach(header => { + const result = extractLanguageFromAcceptHeader(header); + console.log(`Header: "${header}" -> Language: ${result}`); +}); + +console.log('\n✅ Multi-language system is working correctly!'); +console.log('\nFeatures implemented:'); +console.log('- Accept-Language header parsing with quality values'); +console.log('- Support for EN, HU, DE templates'); +console.log('- Custom header detection (X-Language, X-Region, X-Locale)'); +console.log('- Fallback to English for unsupported languages'); +console.log('- Professional email templates in all three languages'); diff --git a/SerpentRace_Backend/package.json b/SerpentRace_Backend/package.json index b0ce278f..2639da2e 100644 --- a/SerpentRace_Backend/package.json +++ b/SerpentRace_Backend/package.json @@ -40,9 +40,6 @@ "watch": "tsc --watch" }, "dependencies": { - "@types/multer": "^2.0.0", - "@types/nodemailer": "^7.0.1", - "@types/uuid": "^10.0.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", "express": "^5.1.0", @@ -62,6 +59,9 @@ "swagger-ui-express": "^5.0.1" }, "devDependencies": { + "@types/multer": "^2.0.0", + "@types/nodemailer": "^7.0.1", + "@types/uuid": "^10.0.0", "@jest/globals": "^30.0.5", "@types/bcrypt": "^6.0.0", "@types/cookie-parser": "^1.4.9", diff --git a/SerpentRace_Backend/src/Api/index.ts b/SerpentRace_Backend/src/Api/index.ts index 9fedf8eb..ee8376f0 100644 --- a/SerpentRace_Backend/src/Api/index.ts +++ b/SerpentRace_Backend/src/Api/index.ts @@ -10,7 +10,10 @@ import chatRouter from './routers/chatRouter'; import contactRouter from './routers/contactRouter'; import adminRouter from './routers/adminRouter'; import deckImportExportRouter from './routers/deckImportExportRouter'; +<<<<<<< HEAD import gameRouter from './routers/gameRouter'; +======= +>>>>>>> origin/main import { LoggingService, logStartup, logConnection, logError, logRequest } from '../Application/Services/Logger'; import { WebSocketService } from '../Application/Services/WebSocketService'; import { setupSwagger } from './swagger/swaggerUiSetup'; @@ -132,7 +135,10 @@ app.use('/api/chats', chatRouter); app.use('/api/contacts', contactRouter); app.use('/api/admin', adminRouter); app.use('/api/deck-import-export', deckImportExportRouter); +<<<<<<< HEAD app.use('/api/games', gameRouter); +======= +>>>>>>> origin/main // Global error handler (must be after routes) app.use(loggingService.errorLoggingMiddleware()); diff --git a/SerpentRace_Backend/src/Api/routers/adminRouter.ts b/SerpentRace_Backend/src/Api/routers/adminRouter.ts index d9e62618..206a94a0 100644 --- a/SerpentRace_Backend/src/Api/routers/adminRouter.ts +++ b/SerpentRace_Backend/src/Api/routers/adminRouter.ts @@ -107,6 +107,41 @@ router.get('/users/page/:from/:to', adminRequired, async (req: Request, res: Res } }); +<<<<<<< HEAD +======= +// Get users by page (admin only) - RECOMMENDED +router.get('/users/page/:from/:to', adminRequired, async (req: Request, res: Response) => { + try { + const from = parseInt(req.params.from); + const to = parseInt(req.params.to); + const includeDeleted = req.query.includeDeleted === 'true'; + + if (isNaN(from) || isNaN(to) || from < 0 || to < from) { + return res.status(400).json({ error: 'Invalid page parameters. "from" and "to" must be valid numbers with to >= from >= 0' }); + } + + logRequest('Admin get users by page endpoint accessed', req, res, { from, to, includeDeleted }); + + const result = includeDeleted + ? await container.userRepository.findByPageIncludingDeleted(from, to) + : await container.userRepository.findByPage(from, to); + + logRequest('Admin users page retrieved successfully', req, res, { + from, + to, + count: result.users.length, + total: result.totalCount, + includeDeleted + }); + + res.json(result); + } catch (error) { + logError('Admin get users by page endpoint error', error as Error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +>>>>>>> origin/main // Get user by ID including soft-deleted ones router.get('/users/:userId', adminRequired, @@ -141,6 +176,7 @@ router.get('/users/:userId', }); // Search users including soft-deleted ones +<<<<<<< HEAD // router.get('/users/search/:searchTerm', // adminRequired, // ValidationMiddleware.validateStringLength({ searchTerm: { min: 2, max: 100 } }), @@ -167,6 +203,34 @@ router.get('/users/:userId', // res.status(500).json({ error: 'Internal server error' }); // } // }); +======= +router.get('/users/search/:searchTerm', + adminRequired, + ValidationMiddleware.validateStringLength({ searchTerm: { min: 2, max: 100 } }), + async (req: Request, res: Response) => { + try { + const { searchTerm } = req.params; + const includeDeleted = req.query.includeDeleted === 'true'; + + logRequest('Admin search users endpoint accessed', req, res, { searchTerm, includeDeleted }); + + const users = includeDeleted + ? await container.userRepository.searchIncludingDeleted(searchTerm) + : await container.userRepository.search(searchTerm); + + logRequest('Admin user search completed', req, res, { + searchTerm, + resultCount: Array.isArray(users) ? users.length : (users.totalCount || 0), + includeDeleted + }); + + res.json(users); + } catch (error) { + logError('Admin search users endpoint error', error as Error, req, res); + res.status(500).json({ error: 'Internal server error' }); + } +}); +>>>>>>> origin/main // Update any user (admin only) router.patch('/users/:userId', @@ -358,6 +422,7 @@ router.get('/decks/search/:searchTerm', adminRequired, async (req: Request, res: } }); +<<<<<<< HEAD //modify deck (admin only) router.patch('/decks/:id', adminRequired, async (req: Request, res: Response) => { try { @@ -382,6 +447,8 @@ router.patch('/decks/:id', adminRequired, async (req: Request, res: Response) => } }); +======= +>>>>>>> origin/main // Hard delete deck (admin only) router.delete('/decks/:id/hard', adminRequired, async (req: Request, res: Response) => { try { diff --git a/SerpentRace_Backend/src/Api/routers/deckRouter.ts b/SerpentRace_Backend/src/Api/routers/deckRouter.ts index 05181b90..03bb3eab 100644 --- a/SerpentRace_Backend/src/Api/routers/deckRouter.ts +++ b/SerpentRace_Backend/src/Api/routers/deckRouter.ts @@ -60,7 +60,11 @@ deckRouter.post('/', authRequired, async (req, res) => { try { const userId = (req as any).user.userId; logRequest('Create deck endpoint accessed', req, res, { name: req.body.name, userId }); +<<<<<<< HEAD req.body.userid = userId; // Set userId in request body +======= + +>>>>>>> origin/main const result = await container.createDeckCommandHandler.execute(req.body); logRequest('Deck created successfully', req, res, { deckId: result.id, name: req.body.name, userId }); @@ -140,7 +144,11 @@ deckRouter.get('/:id', authRequired, async (req, res) => { } }); +<<<<<<< HEAD deckRouter.patch('/:id', authRequired, async (req, res) => { +======= +deckRouter.put('/:id', authRequired, async (req, res) => { +>>>>>>> origin/main try { const deckId = req.params.id; const userId = (req as any).user.userId; @@ -164,10 +172,13 @@ deckRouter.patch('/:id', authRequired, async (req, res) => { if (error instanceof Error && error.message.includes('validation')) { return res.status(400).json({ error: 'Invalid input data', details: error.message }); } +<<<<<<< HEAD if (error instanceof Error && error.message.includes('admin')) { return res.status(403).json({ error: 'Forbidden: ' + error.message }); } +======= +>>>>>>> origin/main res.status(500).json({ error: 'Internal server error' }); } diff --git a/SerpentRace_Backend/src/Api/routers/userRouter.ts b/SerpentRace_Backend/src/Api/routers/userRouter.ts index 0ff5fe98..5e421984 100644 --- a/SerpentRace_Backend/src/Api/routers/userRouter.ts +++ b/SerpentRace_Backend/src/Api/routers/userRouter.ts @@ -32,7 +32,11 @@ userRouter.post('/login', logAuth('User login successful', result.user.id, { username: result.user.username }, req, res); res.json(result); } else { +<<<<<<< HEAD throw new Error(`Login failed: ${result}`); +======= + return ErrorResponseService.sendUnauthorized(res, 'Invalid username or password'); +>>>>>>> origin/main } } catch (error) { @@ -48,9 +52,12 @@ userRouter.post('/login', if (error.message.includes('not verified')) { return ErrorResponseService.sendUnauthorized(res, 'Please verify your email address'); } +<<<<<<< HEAD if (error.message.includes('restriction')) { return ErrorResponseService.sendUnauthorized(res, 'Please verify your email address'); } +======= +>>>>>>> origin/main if (error.message.includes('deactivated')) { return ErrorResponseService.sendUnauthorized(res, 'Account has been deactivated'); } @@ -87,8 +94,12 @@ userRouter.post('/create', res.status(201).json(result); } catch (error) { +<<<<<<< HEAD // Don't log here since CreateUserCommandHandler already logs system errors // Only log validation/user input errors at router level +======= + logError('Create user endpoint error', error as Error, req, res); +>>>>>>> origin/main if (error instanceof Error) { if (error.message.includes('already exists')) { @@ -97,10 +108,13 @@ userRouter.post('/create', if (error.message.includes('validation')) { return ErrorResponseService.sendBadRequest(res, error.message); } +<<<<<<< HEAD // Log unexpected errors that weren't handled by the command handler if (!error.message.includes('Failed to create user')) { logError('Unexpected create user endpoint error', error as Error, req, res); } +======= +>>>>>>> origin/main } return ErrorResponseService.sendInternalServerError(res); @@ -173,6 +187,7 @@ userRouter.patch('/profile', authRequired, async (req, res) => { } }); +<<<<<<< HEAD //Soft delete user (current user) userRouter.delete('/profile', authRequired, async (req, res) => { try { @@ -310,4 +325,6 @@ userRouter.post('/reset-password', } }); +======= +>>>>>>> origin/main export default userRouter; diff --git a/SerpentRace_Backend/src/Api/swagger/swaggerConfig.ts b/SerpentRace_Backend/src/Api/swagger/swaggerConfig.ts index 1f2e31ef..e159bd9e 100644 --- a/SerpentRace_Backend/src/Api/swagger/swaggerConfig.ts +++ b/SerpentRace_Backend/src/Api/swagger/swaggerConfig.ts @@ -1,5 +1,8 @@ import swaggerJSDoc from 'swagger-jsdoc'; +<<<<<<< HEAD import path from 'path'; +======= +>>>>>>> origin/main export const swaggerOptions = { definition: { @@ -19,12 +22,17 @@ export const swaggerOptions = { }, servers: [ { +<<<<<<< HEAD url: 'http://localhost:3001', description: 'Local development server' }, { url: 'http://localhost:3000', description: 'Local development server (alt)' +======= + url: 'http://localhost:3000', + description: 'Local development server' +>>>>>>> origin/main }, { url: 'https://api.serpentrace.com', @@ -66,6 +74,7 @@ export const swaggerOptions = { { name: 'Deck Import/Export', description: 'Import and export deck functionality' +<<<<<<< HEAD }, { name: 'Games', @@ -90,11 +99,17 @@ export const swaggerOptions = { { name: 'Admin - Contacts', description: 'Admin contact management operations' +======= +>>>>>>> origin/main } ] }, apis: [ +<<<<<<< HEAD './src/Api/swagger/swaggerDefinitionsFixed.ts' +======= + './src/Api/swagger/swaggerDefinitions.ts' +>>>>>>> origin/main ], }; diff --git a/SerpentRace_Backend/src/Api/swagger/swaggerDefinitions.ts b/SerpentRace_Backend/src/Api/swagger/swaggerDefinitions.ts index 0a2ca58f..b69dd565 100644 --- a/SerpentRace_Backend/src/Api/swagger/swaggerDefinitions.ts +++ b/SerpentRace_Backend/src/Api/swagger/swaggerDefinitions.ts @@ -1,6 +1,14 @@ /** * @swagger * components: +<<<<<<< HEAD +======= + * securitySchemes: + * bearerAuth: + * type: http + * scheme: bearer + * bearerFormat: JWT +>>>>>>> origin/main * schemas: * User: * type: object @@ -100,6 +108,7 @@ * type: string * format: email * +<<<<<<< HEAD * ForgotPasswordRequest: * type: object * required: @@ -131,6 +140,8 @@ * message: * type: string * +======= +>>>>>>> origin/main * Organization: * type: object * properties: @@ -325,6 +336,7 @@ * chatId: * type: string * +<<<<<<< HEAD * Game: * type: object * properties: @@ -353,6 +365,8 @@ * type: string * format: date-time * +======= +>>>>>>> origin/main * Error: * type: object * properties: @@ -363,6 +377,7 @@ * format: date-time * details: * type: string +<<<<<<< HEAD */ /** * @swagger @@ -392,6 +407,34 @@ * schema: * $ref: '#/components/schemas/Error' * +======= + * + * paths: + * /api/users/login: + * post: + * tags: [Users] + * summary: User login + * description: Authenticate user and return JWT token + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginRequest' + * responses: + * 200: + * description: Login successful + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginResponse' + * 401: + * description: Invalid credentials + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Error' +>>>>>>> origin/main * * /api/users/create: * post: @@ -1454,6 +1497,7 @@ * application/json: * schema: * $ref: '#/components/schemas/Contact' +<<<<<<< HEAD * * /api/games/start: * post: @@ -1611,6 +1655,8 @@ * description: Game already started or not ready to start * 500: * description: Internal server error +======= +>>>>>>> origin/main */ export {}; diff --git a/SerpentRace_Backend/src/Application/DTOs/Mappers/UserMapper.ts b/SerpentRace_Backend/src/Application/DTOs/Mappers/UserMapper.ts index 294022af..475ef793 100644 --- a/SerpentRace_Backend/src/Application/DTOs/Mappers/UserMapper.ts +++ b/SerpentRace_Backend/src/Application/DTOs/Mappers/UserMapper.ts @@ -21,6 +21,10 @@ export class UserMapper { fname: user.fname, lname: user.lname, code: user.token, +<<<<<<< HEAD +======= + type: user.type, +>>>>>>> origin/main phone: user.phone, state: user.state, }; diff --git a/SerpentRace_Backend/src/Application/DTOs/UserDto.ts b/SerpentRace_Backend/src/Application/DTOs/UserDto.ts index 0bcd58a0..8ede17cf 100644 --- a/SerpentRace_Backend/src/Application/DTOs/UserDto.ts +++ b/SerpentRace_Backend/src/Application/DTOs/UserDto.ts @@ -24,6 +24,10 @@ export interface DetailUserDto { fname: string; lname: string; code: string | null; +<<<<<<< HEAD +======= + type: string; +>>>>>>> origin/main phone: string | null; state: number; } diff --git a/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommand.ts b/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommand.ts index 3121272c..03eec491 100644 --- a/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommand.ts +++ b/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommand.ts @@ -1,6 +1,9 @@ export interface UpdateDeckCommand { id: string; +<<<<<<< HEAD userstate?: number; +======= +>>>>>>> origin/main name?: string; type?: number; userid?: string; diff --git a/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommandHandler.ts b/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommandHandler.ts index ced487ce..9e62d817 100644 --- a/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommandHandler.ts +++ b/SerpentRace_Backend/src/Application/Deck/commands/UpdateDeckCommandHandler.ts @@ -2,13 +2,17 @@ import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; import { UpdateDeckCommand } from './UpdateDeckCommand'; import { ShortDeckDto } from '../../DTOs/DeckDto'; import { DeckMapper } from '../../DTOs/Mappers/DeckMapper'; +<<<<<<< HEAD import { DeckAggregate } from '../../../Domain/Deck/DeckAggregate'; import { logError } from '../../Services/Logger'; +======= +>>>>>>> origin/main export class UpdateDeckCommandHandler { constructor(private readonly deckRepo: IDeckRepository) {} async execute(cmd: UpdateDeckCommand): Promise { +<<<<<<< HEAD if(cmd.state !== undefined && cmd.userstate!==1) { throw new Error('Only admin users can change deck state'); } @@ -46,5 +50,10 @@ export class UpdateDeckCommandHandler { logError(`Error updating deck: ${cmd.id}`, error); throw error; } +======= + const updated = await this.deckRepo.update(cmd.id, { ...cmd }); + if (!updated) return null; + return DeckMapper.toShortDto(updated); +>>>>>>> origin/main } } diff --git a/SerpentRace_Backend/src/Application/Deck/queries/GetDeckByIdQueryHandler.ts b/SerpentRace_Backend/src/Application/Deck/queries/GetDeckByIdQueryHandler.ts index 9ea429b4..8a1dd6f7 100644 --- a/SerpentRace_Backend/src/Application/Deck/queries/GetDeckByIdQueryHandler.ts +++ b/SerpentRace_Backend/src/Application/Deck/queries/GetDeckByIdQueryHandler.ts @@ -1,14 +1,25 @@ import { IDeckRepository } from '../../../Domain/IRepository/IDeckRepository'; import { GetDeckByIdQuery } from './GetDeckByIdQuery'; +<<<<<<< HEAD import { DetailDeckDto } from '../../DTOs/DeckDto'; +======= +import { ShortDeckDto } from '../../DTOs/DeckDto'; +>>>>>>> origin/main import { DeckMapper } from '../../DTOs/Mappers/DeckMapper'; export class GetDeckByIdQueryHandler { constructor(private readonly deckRepo: IDeckRepository) {} +<<<<<<< HEAD async execute(query: GetDeckByIdQuery): Promise { const deck = await this.deckRepo.findById(query.id); if (!deck) return null; return DeckMapper.toDetailDto(deck); +======= + async execute(query: GetDeckByIdQuery): Promise { + const deck = await this.deckRepo.findById(query.id); + if (!deck) return null; + return DeckMapper.toShortDto(deck); +>>>>>>> origin/main } } diff --git a/SerpentRace_Backend/src/Application/Search/Generalsearch.ts b/SerpentRace_Backend/src/Application/Search/Generalsearch.ts index 0d940191..e97ee512 100644 --- a/SerpentRace_Backend/src/Application/Search/Generalsearch.ts +++ b/SerpentRace_Backend/src/Application/Search/Generalsearch.ts @@ -49,6 +49,7 @@ export class GeneralSearchService implements IGeneralSearchService { }; } +<<<<<<< HEAD // Ensure limit is at least 1 to prevent database issues const effectiveLimit = Math.max(limit || 20, 1); const effectiveOffset = Math.max(offset || 0, 0); @@ -57,6 +58,12 @@ export class GeneralSearchService implements IGeneralSearchService { const { users, totalCount } = await this.userRepo.search(query.trim(), effectiveLimit, effectiveOffset); const results = users.map(user => UserMapper.toShortDto(user)); const hasMore = (effectiveOffset + effectiveLimit) < totalCount; +======= + try { + const { users, totalCount } = await this.userRepo.search(query.trim(), limit, offset); + const results = users.map(user => UserMapper.toShortDto(user)); + const hasMore = (offset + limit) < totalCount; +>>>>>>> origin/main return { results, @@ -109,6 +116,7 @@ export class GeneralSearchService implements IGeneralSearchService { }; } +<<<<<<< HEAD // Ensure limit is at least 1 to prevent database issues const effectiveLimit = Math.max(limit || 20, 1); const effectiveOffset = Math.max(offset || 0, 0); @@ -128,6 +136,19 @@ export class GeneralSearchService implements IGeneralSearchService { } catch (error) { throw new Error('Failed to search decks'); } +======= + const { decks, totalCount } = await this.deckRepo.search(query.trim(), limit, offset); + const results = decks.map(deck => DeckMapper.toShortDto(deck)); + const hasMore = (offset + limit) < totalCount; + + return { + results, + totalCount, + hasMore, + searchQuery: query, + searchType: 'decks' + }; +>>>>>>> origin/main } async searchByType( diff --git a/SerpentRace_Backend/src/Application/Services/AuthMiddleware.ts b/SerpentRace_Backend/src/Application/Services/AuthMiddleware.ts index 0bc84dcc..b3ba3a0c 100644 --- a/SerpentRace_Backend/src/Application/Services/AuthMiddleware.ts +++ b/SerpentRace_Backend/src/Application/Services/AuthMiddleware.ts @@ -1,5 +1,6 @@ import { Request, Response, NextFunction } from 'express'; import { JWTService } from './JWTService'; +<<<<<<< HEAD import { RedisService } from './RedisService'; import { logAuth, logWarning } from './Logger'; @@ -143,4 +144,60 @@ export async function adminRequired(req: Request, res: Response, next: NextFunct logWarning('Admin authentication middleware error', { error: (error as Error).message }, req); return res.status(500).json({ error: 'Internal server error' }); } +======= +import { logAuth, logWarning } from './Logger'; + +export const jwtService = new JWTService(); + +export function authRequired(req: Request, res: Response, next: NextFunction) { + const payload = jwtService.verify(req); + if (!payload) { + logAuth('Authentication failed - No valid token', undefined, { + ip: req.ip, + userAgent: req.get ? req.get('User-Agent') : 'unknown', + path: req.path + }, req); + return res.status(401).json({ error: 'Unauthorized' }); + } + + logAuth('Authentication successful', payload.userId, { + authLevel: payload.authLevel, + orgId: payload.orgId + }, req); + + const refreshed = jwtService.refreshIfNeeded(payload, res); + if (refreshed) { + logAuth('Token refreshed', payload.userId, undefined, req); + } + + (req as any).user = payload; + next(); +} + +export function adminRequired(req: Request, res: Response, next: NextFunction) { + const payload = jwtService.verify(req); + if (!payload || payload.authLevel !== 1) { + logWarning('Admin access denied', { + hasPayload: !!payload, + authLevel: payload?.authLevel, + userId: payload?.userId, + ip: req.ip, + path: req.path + }, req); + return res.status(403).json({ error: 'Forbidden' }); + } + + logAuth('Admin authentication successful', payload.userId, { + authLevel: payload.authLevel, + orgId: payload.orgId + }, req); + + const refreshed = jwtService.refreshIfNeeded(payload, res); + if (refreshed) { + logAuth('Admin token refreshed', payload.userId, undefined, req); + } + + (req as any).user = payload; + next(); +>>>>>>> origin/main } \ No newline at end of file diff --git a/SerpentRace_Backend/src/Infrastructure/Migrations/1755691733404-test.ts b/SerpentRace_Backend/src/Infrastructure/Migrations/1755691733404-test.ts new file mode 100644 index 00000000..853b8a3c --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrations/1755691733404-test.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Test1755691733404 implements MigrationInterface { + name = 'Test1755691733404' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "Users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "orgid" uuid, "username" character varying(100) NOT NULL, "password" character varying(255) NOT NULL, "email" character varying(255) NOT NULL, "fname" character varying(100) NOT NULL, "lname" character varying(100) NOT NULL, "code" character varying(50), "type" character varying(50) NOT NULL, "phone" character varying(20), "state" integer NOT NULL DEFAULT '0', "regdate" TIMESTAMP NOT NULL DEFAULT now(), "updatedate" TIMESTAMP NOT NULL DEFAULT now(), "Orglogindate" TIMESTAMP, CONSTRAINT "UQ_ffc81a3b97dcbf8e320d5106c0d" UNIQUE ("username"), CONSTRAINT "UQ_3c3ab3f49a87e6ddb607f3c4945" UNIQUE ("email"), CONSTRAINT "PK_16d4f7d636df336db11d87413e3" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "Organizations" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "contactfname" character varying(100) NOT NULL, "contactlname" character varying(100) NOT NULL, "contactphone" character varying(20) NOT NULL, "contactemail" character varying(255) NOT NULL, "state" integer NOT NULL DEFAULT '0', "regdate" TIMESTAMP NOT NULL DEFAULT now(), "updatedate" TIMESTAMP NOT NULL DEFAULT now(), "url" character varying(500), "userinorg" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_e0690a31419f6666194423526f2" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "Decks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "type" integer NOT NULL, "user_id" uuid NOT NULL, "creation_date" TIMESTAMP NOT NULL DEFAULT now(), "cards" json NOT NULL, "played_number" integer NOT NULL DEFAULT '0', "ctype" integer NOT NULL DEFAULT '0', "update_date" TIMESTAMP NOT NULL DEFAULT now(), "state" integer NOT NULL DEFAULT '0', "organization_id" uuid, CONSTRAINT "PK_001f26cb3ec39c1f25269943473" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "Chats" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "users" uuid array NOT NULL, "messages" json NOT NULL, "updateDate" TIMESTAMP NOT NULL DEFAULT now(), "state" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_64c36c2b8d86a0d5de4cf64de8d" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TABLE "Decks" ADD CONSTRAINT "FK_06ee28f90d68543a03b14aebe13" FOREIGN KEY ("organization_id") REFERENCES "Organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "Decks" DROP CONSTRAINT "FK_06ee28f90d68543a03b14aebe13"`); + await queryRunner.query(`DROP TABLE "Chats"`); + await queryRunner.query(`DROP TABLE "Decks"`); + await queryRunner.query(`DROP TABLE "Organizations"`); + await queryRunner.query(`DROP TABLE "Users"`); + } + +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.ts b/SerpentRace_Backend/src/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.ts new file mode 100644 index 00000000..a57a12c5 --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrations/1755706019351-AddEmailVerificationFields.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddEmailVerificationFields1755706019351 implements MigrationInterface { + name = 'AddEmailVerificationFields1755706019351' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "Users" DROP COLUMN "code"`); + await queryRunner.query(`ALTER TABLE "Users" ADD "token" character varying(255)`); + await queryRunner.query(`ALTER TABLE "Users" ADD "TokenExpires" TIMESTAMP`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "Users" DROP COLUMN "TokenExpires"`); + await queryRunner.query(`ALTER TABLE "Users" DROP COLUMN "token"`); + await queryRunner.query(`ALTER TABLE "Users" ADD "code" character varying(50)`); + } + +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.ts b/SerpentRace_Backend/src/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.ts new file mode 100644 index 00000000..8da0576f --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrations/1755817306222-AddChatMessagingSystem.ts @@ -0,0 +1,30 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddChatMessagingSystem1755817306222 implements MigrationInterface { + name = 'AddChatMessagingSystem1755817306222' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "ChatArchives" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "chatId" uuid NOT NULL, "archivedMessages" json NOT NULL, "archivedAt" TIMESTAMP NOT NULL, "createDate" TIMESTAMP NOT NULL DEFAULT now(), "chatType" character varying(50) NOT NULL, "chatName" character varying(255), "gameId" uuid, "participants" uuid array NOT NULL, CONSTRAINT "PK_fe62979fc2061d7afe278d3f14e" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "type" character varying(50) NOT NULL DEFAULT 'direct'`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "name" character varying(255)`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "gameId" uuid`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "createdBy" uuid`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "lastActivity" TIMESTAMP`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "createDate" TIMESTAMP NOT NULL DEFAULT now()`); + await queryRunner.query(`ALTER TABLE "Chats" ADD "archiveDate" TIMESTAMP`); + await queryRunner.query(`ALTER TABLE "Chats" ALTER COLUMN "messages" SET DEFAULT '[]'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "Chats" ALTER COLUMN "messages" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "archiveDate"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "createDate"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "lastActivity"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "createdBy"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "gameId"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "name"`); + await queryRunner.query(`ALTER TABLE "Chats" DROP COLUMN "type"`); + await queryRunner.query(`DROP TABLE "ChatArchives"`); + } + +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrations/1755855028839-CreateContactTable.ts b/SerpentRace_Backend/src/Infrastructure/Migrations/1755855028839-CreateContactTable.ts new file mode 100644 index 00000000..ab9fd2dd --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrations/1755855028839-CreateContactTable.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class CreateContactTable1755855028839 implements MigrationInterface { + name = 'CreateContactTable1755855028839' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "Contacts" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "email" character varying(255) NOT NULL, "userid" uuid, "type" integer NOT NULL, "txt" text NOT NULL, "state" integer NOT NULL DEFAULT '0', "createDate" TIMESTAMP NOT NULL DEFAULT now(), "updateDate" TIMESTAMP NOT NULL DEFAULT now(), "adminResponse" text, "responseDate" TIMESTAMP, "respondedBy" uuid, CONSTRAINT "PK_68782cec65c8eef577c62958273" PRIMARY KEY ("id"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "Contacts"`); + } + +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.ts b/SerpentRace_Backend/src/Infrastructure/Migrations/1755905000000-AddStateToChatArchives.ts new file mode 100644 index 00000000..e69de29b diff --git a/SerpentRace_Backend/src/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.ts b/SerpentRace_Backend/src/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.ts new file mode 100644 index 00000000..e9f533b2 --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrations/AddMaxOrganizationalDecksToOrganization.ts @@ -0,0 +1,28 @@ +import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'; + +export class AddMaxOrganizationalDecksToOrganization1692712800000 implements MigrationInterface { + name = 'AddMaxOrganizationalDecksToOrganization1692712800000'; + + public async up(queryRunner: QueryRunner): Promise { + // Add maxOrganizationalDecks column to Organizations table + await queryRunner.addColumn('Organizations', new TableColumn({ + name: 'maxOrganizationalDecks', + type: 'int', + isNullable: true, // No default - set by admin + comment: 'Maximum number of organizational decks a premium user can create in this organization' + })); + + // Add performance indexes for deck filtering queries + await queryRunner.query(`CREATE INDEX "IDX_DECK_USER_STATE_CTYPE" ON "Decks" ("user_id", "state", "ctype")`); + await queryRunner.query(`CREATE INDEX "IDX_DECK_ORG_CTYPE_STATE" ON "Decks" ("organization_id", "ctype", "state")`); + } + + public async down(queryRunner: QueryRunner): Promise { + // Drop indexes + await queryRunner.query(`DROP INDEX "IDX_DECK_ORG_CTYPE_STATE"`); + await queryRunner.query(`DROP INDEX "IDX_DECK_USER_STATE_CTYPE"`); + + // Remove maxOrganizationalDecks column + await queryRunner.dropColumn('Organizations', 'maxOrganizationalDecks'); + } +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755691732089-test.ts b/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755691732089-test.ts new file mode 100644 index 00000000..1c470ad3 --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755691732089-test.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Test1755691732089 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.ts b/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.ts new file mode 100644 index 00000000..c2a5eda1 --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755706017175-AddEmailVerificationFields.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddEmailVerificationFields1755706017175 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.ts b/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.ts new file mode 100644 index 00000000..93e9a570 --- /dev/null +++ b/SerpentRace_Backend/src/Infrastructure/Migrationsettings/1755706055220-FixEmailVerificationFields.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class FixEmailVerificationFields1755706055220 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/SerpentRace_Backend/test-org-auth.js b/SerpentRace_Backend/test-org-auth.js new file mode 100644 index 00000000..4e627ad2 --- /dev/null +++ b/SerpentRace_Backend/test-org-auth.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +/** + * Test script for Organization Authentication functionality + * This script tests the new organization authentication features: + * 1. Get organization login URL + * 2. Process third-party authentication callback + * 3. Login with organization reauthentication check + */ + +const { container } = require('./dist/Application/Services/DIContainer.js'); + +async function testOrganizationAuth() { + console.log('🧪 Testing Organization Authentication Functionality\n'); + + try { + // Test 1: Get Organization Login URL + console.log('1️⃣ Testing Get Organization Login URL Query Handler'); + const getUrlHandler = container.getOrganizationLoginUrlQueryHandler; + console.log('✅ Handler instantiated successfully'); + + // Test 2: Process Organization Auth Callback + console.log('2️⃣ Testing Process Organization Auth Callback Command Handler'); + const callbackHandler = container.processOrgAuthCallbackCommandHandler; + console.log('✅ Handler instantiated successfully'); + + // Test 3: Enhanced Login Handler with Organization Repository + console.log('3️⃣ Testing Enhanced Login Handler'); + const loginHandler = container.loginCommandHandler; + console.log('✅ Enhanced login handler instantiated successfully'); + + console.log('\n🎉 All Organization Authentication components initialized successfully!'); + console.log('\n📋 Summary of new functionality:'); + console.log(' • GET /api/organizations/:orgId/login-url - Get organization third-party login URL'); + console.log(' • POST /api/organizations/auth-callback - Process third-party authentication result'); + console.log(' • Enhanced login response includes organization reauthentication requirements'); + console.log(' • Users must reauthenticate with organization if last login > 1 month ago'); + + } catch (error) { + console.error('❌ Error testing organization authentication:', error.message); + process.exit(1); + } +} + +// Run the test +testOrganizationAuth(); diff --git a/SerpentRace_Docker/.env.example b/SerpentRace_Docker/.env.example index b259884b..a1e8afbf 100644 --- a/SerpentRace_Docker/.env.example +++ b/SerpentRace_Docker/.env.example @@ -219,4 +219,4 @@ EMAIL_DEBUG_MODE=true # - EMAIL_HOST, EMAIL_PORT, EMAIL_USER, EMAIL_PASS # OPTIONAL VARIABLES: -# All other variables have sensible defaults and are optional \ No newline at end of file +# All other variables have sensible defaults and are optional diff --git a/SerpentRace_Frontend/package.json b/SerpentRace_Frontend/package.json index 6bf68c49..9efa2b81 100644 --- a/SerpentRace_Frontend/package.json +++ b/SerpentRace_Frontend/package.json @@ -16,17 +16,17 @@ "react-dom": "^19.1.0", "react-icons": "^5.5.0", "react-router-dom": "^7.6.0", - "tailwindcss": "^4.1.7", - "vite": "^6.3.5", - "eslint-plugin-react-hooks": "^5.2.0", - "@vitejs/plugin-react": "^4.4.1", - "eslint-plugin-react-refresh": "^0.4.19" + "tailwindcss": "^4.1.7" }, "devDependencies": { "@eslint/js": "^9.25.0", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", "eslint": "^9.25.0", - "globals": "^16.0.0" + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.5" } } diff --git a/SerpentRace_Frontend/src/pages/About/About.jsx b/SerpentRace_Frontend/src/pages/About/About.jsx index 273196ab..f87c9257 100644 --- a/SerpentRace_Frontend/src/pages/About/About.jsx +++ b/SerpentRace_Frontend/src/pages/About/About.jsx @@ -2,13 +2,13 @@ import React, { useEffect, useRef, useState } from "react" import Navbar from "../../components/Navbar/Navbar" import Footer from "../../components/Footer/Footer" import Background from "../../assets/backgrounds/Background.jsx" -import Walke from "../../assets/pictures/walke.JPG" -import Busi from "../../assets/pictures/busi.JPG" -import Gege from "../../assets/pictures/gege.JPG" -import Zsola from "../../assets/pictures/zsola.JPG" -import Donat from "../../assets/pictures/donat.JPG" -import Turo from "../../assets/pictures/turo.JPG" -import Piskor from "../../assets/pictures/piskor.JPG" +import Walke from "../../assets/pictures/walke.jpg" +import Busi from "../../assets/pictures/busi.jpg" +import Gege from "../../assets/pictures/gege.jpg" +import Zsola from "../../assets/pictures/zsola.jpg" +import Donat from "../../assets/pictures/donat.jpg" +import Turo from "../../assets/pictures/turo.jpg" +import Piskor from "../../assets/pictures/piskor.jpg" const About = () => { const [visible, setVisible] = useState(false) diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 00000000..cf18be3d --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,55 @@ +{ + "name": "SerpentRace", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/framer-motion": { + "version": "12.23.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.0.tgz", + "integrity": "sha512-xf6NxTGAyf7zR4r2KlnhFmsRfKIbjqeBupEDBAaEtVIBJX96sAon00kMlsKButSIRwPSHjbRrAPnYdJJ9kyhbA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.22.0", + "motion-utils": "^12.19.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.22.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.22.0.tgz", + "integrity": "sha512-ooH7+/BPw9gOsL9VtPhEJHE2m4ltnhMlcGMhEqA0YGNhKof7jdaszvsyThXI6LVIKshJUZ9/CP6HNqQhJfV7kw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.19.0" + } + }, + "node_modules/motion-utils": { + "version": "12.19.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.19.0.tgz", + "integrity": "sha512-BuFTHINYmV07pdWs6lj6aI63vr2N4dg0vR+td0rtrdpWOhBzIkEklZyLcvKBoEtwSqx8Jg06vUB5RS0xDiUybw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + } + } +} diff --git a/node_modules/framer-motion/LICENSE.md b/node_modules/framer-motion/LICENSE.md new file mode 100644 index 00000000..b5b8d6a8 --- /dev/null +++ b/node_modules/framer-motion/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Framer B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/framer-motion/README.md b/node_modules/framer-motion/README.md new file mode 100644 index 00000000..a8ece514 --- /dev/null +++ b/node_modules/framer-motion/README.md @@ -0,0 +1,137 @@ +

+ Motion logo +

+

Motion for React

+

+ An open source, production-ready animation library +

+

Previously Framer Motion. Also available for JavaScript and Vue

+ +
+ +

+ + + + + + + + + +

+ +
+ +Motion for React is an open source, production-ready library that’s designed for all creative developers. + +It's the only animation library with a hybrid engine, combining the power of JavaScript animations with the performance of native browser APIs. + +It looks like this: + +```jsx + +``` + +It does all this: + +- [Springs](https://motion.dev/docs/react-transitions#spring) +- [Keyframes](https://motion.dev/docs/react-animation#keyframes) +- [Layout animations](https://motion.dev/docs/react-layout-animations) +- [Shared layout animations](https://motion.dev/docs/react-layout-animations#shared-layout-animations) +- [Gestures (drag/tap/hover)](https://motion.dev/docs/react-gestures) +- [Scroll animations](https://motion.dev/docs/react-scroll-animations) +- [SVG paths](https://motion.dev/docs/react-animation#svg-line-drawing) +- [Exit animations](https://motion.dev/docs/react-animation#exit-animations) +- [Server-side rendering](https://motion.dev/docs/react-motion-component#server-side-rendering) +- [Independent transforms](https://motion.dev/docs/react-motion-component#style) +- [Orchestrate animations across components](https://motion.dev/docs/react-animation#orchestration) +- [CSS variables](https://motion.dev/docs/react-animation#css-variables) + +...and a whole lot more. + +## Get started + +### 🐇 Quick start + +```bash +npm install motion +``` + +```jsx +import { motion } from "motion/react" + +function Component() { + return +} +``` + +Get started with [Motion for React](https://motion.dev/docs/react-quick-start). + +## 🎨 Studio + +![Video of bezier curve editing](https://framerusercontent.com/images/KO5dnHOUSNGb9S73p1J7nLhoFI.gif) + +Motion Studio is a versatile suite of developer tools allowing you to: + +- Visually edit CSS and Motion easing curves in VS Code +- Generate CSS springs with LLMs +- Load Motion docs into your LLM + +Get started with [Motion Studio](https://motion.dev/docs/tools-quick-start). + +## 🎓 Examples + +[Motion Examples](https://examples.motion.dev/react) offers 100s of free and Motion+ examples for beginners and advanced users alike. Easy copy/paste code to kickstart your next project. + +## ⚡️ Motion+ + +[Motion+](https://motion.dev/plus) is a one-time fee, lifetime membership that unlocks over 100 premium examples, early access, powerful Studio tools, a private Discord, and exclusive APIs like: + +- Cursor +- Ticker +- AnimateNumber +- splitText + +[Get Motion+](https://motion.dev/plus) + +### 💎 Contribute + +- Want to contribute to Motion? Our [contributing guide](https://github.com/motiondivision/motion/blob/master/CONTRIBUTING.md) has you covered. + +### 👩🏻‍⚖️ License + +- Motion for React is MIT licensed. + +## ✨ Sponsors + +Motion is sustainable thanks to the kind support of its sponsors. + +### Partners + +#### Framer + +Motion powers Framer animations, the web builder for creative pros. Design and ship your dream site. Zero code, maximum speed. + + + Framer + + +### Platinum + +Tailwind Emil Kowalski Linear + +### Gold + +Vercel Liveblocks Luma + +### Silver + +Frontend.fyi Firecrawl Puzzmo Build UI + +### Personal + +- [OlegWock](https://sinja.io) +- [Lambert Weller](https://github.com/l-mbert) +- [Jake LeBoeuf](https://jklb.wf) +- [Han Lee](https://github.com/hahnlee) diff --git a/node_modules/framer-motion/client/README.md b/node_modules/framer-motion/client/README.md new file mode 100644 index 00000000..00af8b44 --- /dev/null +++ b/node_modules/framer-motion/client/README.md @@ -0,0 +1 @@ +This directory is a fallback for `exports["./client"]` in the root `framer-motion` `package.json`. diff --git a/node_modules/framer-motion/client/package.json b/node_modules/framer-motion/client/package.json new file mode 100644 index 00000000..ed702d3c --- /dev/null +++ b/node_modules/framer-motion/client/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "types": "../dist/types/client.d.ts", + "main": "../dist/cjs/client.js", + "module": "../dist/es/client.mjs" +} diff --git a/node_modules/framer-motion/dist/cjs/client.js b/node_modules/framer-motion/dist/cjs/client.js new file mode 100644 index 00000000..af18db28 --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/client.js @@ -0,0 +1,365 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var create = require('./create-C-c1JfhA.js'); +require('motion-dom'); +require('motion-utils'); +require('react/jsx-runtime'); +require('react'); + +/** + * HTML components + */ +const MotionA = /*@__PURE__*/ create.createMotionComponent("a"); +const MotionAbbr = /*@__PURE__*/ create.createMotionComponent("abbr"); +const MotionAddress = /*@__PURE__*/ create.createMotionComponent("address"); +const MotionArea = /*@__PURE__*/ create.createMotionComponent("area"); +const MotionArticle = /*@__PURE__*/ create.createMotionComponent("article"); +const MotionAside = /*@__PURE__*/ create.createMotionComponent("aside"); +const MotionAudio = /*@__PURE__*/ create.createMotionComponent("audio"); +const MotionB = /*@__PURE__*/ create.createMotionComponent("b"); +const MotionBase = /*@__PURE__*/ create.createMotionComponent("base"); +const MotionBdi = /*@__PURE__*/ create.createMotionComponent("bdi"); +const MotionBdo = /*@__PURE__*/ create.createMotionComponent("bdo"); +const MotionBig = /*@__PURE__*/ create.createMotionComponent("big"); +const MotionBlockquote = +/*@__PURE__*/ create.createMotionComponent("blockquote"); +const MotionBody = /*@__PURE__*/ create.createMotionComponent("body"); +const MotionButton = /*@__PURE__*/ create.createMotionComponent("button"); +const MotionCanvas = /*@__PURE__*/ create.createMotionComponent("canvas"); +const MotionCaption = /*@__PURE__*/ create.createMotionComponent("caption"); +const MotionCite = /*@__PURE__*/ create.createMotionComponent("cite"); +const MotionCode = /*@__PURE__*/ create.createMotionComponent("code"); +const MotionCol = /*@__PURE__*/ create.createMotionComponent("col"); +const MotionColgroup = /*@__PURE__*/ create.createMotionComponent("colgroup"); +const MotionData = /*@__PURE__*/ create.createMotionComponent("data"); +const MotionDatalist = /*@__PURE__*/ create.createMotionComponent("datalist"); +const MotionDd = /*@__PURE__*/ create.createMotionComponent("dd"); +const MotionDel = /*@__PURE__*/ create.createMotionComponent("del"); +const MotionDetails = /*@__PURE__*/ create.createMotionComponent("details"); +const MotionDfn = /*@__PURE__*/ create.createMotionComponent("dfn"); +const MotionDialog = /*@__PURE__*/ create.createMotionComponent("dialog"); +const MotionDiv = /*@__PURE__*/ create.createMotionComponent("div"); +const MotionDl = /*@__PURE__*/ create.createMotionComponent("dl"); +const MotionDt = /*@__PURE__*/ create.createMotionComponent("dt"); +const MotionEm = /*@__PURE__*/ create.createMotionComponent("em"); +const MotionEmbed = /*@__PURE__*/ create.createMotionComponent("embed"); +const MotionFieldset = /*@__PURE__*/ create.createMotionComponent("fieldset"); +const MotionFigcaption = +/*@__PURE__*/ create.createMotionComponent("figcaption"); +const MotionFigure = /*@__PURE__*/ create.createMotionComponent("figure"); +const MotionFooter = /*@__PURE__*/ create.createMotionComponent("footer"); +const MotionForm = /*@__PURE__*/ create.createMotionComponent("form"); +const MotionH1 = /*@__PURE__*/ create.createMotionComponent("h1"); +const MotionH2 = /*@__PURE__*/ create.createMotionComponent("h2"); +const MotionH3 = /*@__PURE__*/ create.createMotionComponent("h3"); +const MotionH4 = /*@__PURE__*/ create.createMotionComponent("h4"); +const MotionH5 = /*@__PURE__*/ create.createMotionComponent("h5"); +const MotionH6 = /*@__PURE__*/ create.createMotionComponent("h6"); +const MotionHead = /*@__PURE__*/ create.createMotionComponent("head"); +const MotionHeader = /*@__PURE__*/ create.createMotionComponent("header"); +const MotionHgroup = /*@__PURE__*/ create.createMotionComponent("hgroup"); +const MotionHr = /*@__PURE__*/ create.createMotionComponent("hr"); +const MotionHtml = /*@__PURE__*/ create.createMotionComponent("html"); +const MotionI = /*@__PURE__*/ create.createMotionComponent("i"); +const MotionIframe = /*@__PURE__*/ create.createMotionComponent("iframe"); +const MotionImg = /*@__PURE__*/ create.createMotionComponent("img"); +const MotionInput = /*@__PURE__*/ create.createMotionComponent("input"); +const MotionIns = /*@__PURE__*/ create.createMotionComponent("ins"); +const MotionKbd = /*@__PURE__*/ create.createMotionComponent("kbd"); +const MotionKeygen = /*@__PURE__*/ create.createMotionComponent("keygen"); +const MotionLabel = /*@__PURE__*/ create.createMotionComponent("label"); +const MotionLegend = /*@__PURE__*/ create.createMotionComponent("legend"); +const MotionLi = /*@__PURE__*/ create.createMotionComponent("li"); +const MotionLink = /*@__PURE__*/ create.createMotionComponent("link"); +const MotionMain = /*@__PURE__*/ create.createMotionComponent("main"); +const MotionMap = /*@__PURE__*/ create.createMotionComponent("map"); +const MotionMark = /*@__PURE__*/ create.createMotionComponent("mark"); +const MotionMenu = /*@__PURE__*/ create.createMotionComponent("menu"); +const MotionMenuitem = /*@__PURE__*/ create.createMotionComponent("menuitem"); +const MotionMeter = /*@__PURE__*/ create.createMotionComponent("meter"); +const MotionNav = /*@__PURE__*/ create.createMotionComponent("nav"); +const MotionObject = /*@__PURE__*/ create.createMotionComponent("object"); +const MotionOl = /*@__PURE__*/ create.createMotionComponent("ol"); +const MotionOptgroup = /*@__PURE__*/ create.createMotionComponent("optgroup"); +const MotionOption = /*@__PURE__*/ create.createMotionComponent("option"); +const MotionOutput = /*@__PURE__*/ create.createMotionComponent("output"); +const MotionP = /*@__PURE__*/ create.createMotionComponent("p"); +const MotionParam = /*@__PURE__*/ create.createMotionComponent("param"); +const MotionPicture = /*@__PURE__*/ create.createMotionComponent("picture"); +const MotionPre = /*@__PURE__*/ create.createMotionComponent("pre"); +const MotionProgress = /*@__PURE__*/ create.createMotionComponent("progress"); +const MotionQ = /*@__PURE__*/ create.createMotionComponent("q"); +const MotionRp = /*@__PURE__*/ create.createMotionComponent("rp"); +const MotionRt = /*@__PURE__*/ create.createMotionComponent("rt"); +const MotionRuby = /*@__PURE__*/ create.createMotionComponent("ruby"); +const MotionS = /*@__PURE__*/ create.createMotionComponent("s"); +const MotionSamp = /*@__PURE__*/ create.createMotionComponent("samp"); +const MotionScript = /*@__PURE__*/ create.createMotionComponent("script"); +const MotionSection = /*@__PURE__*/ create.createMotionComponent("section"); +const MotionSelect = /*@__PURE__*/ create.createMotionComponent("select"); +const MotionSmall = /*@__PURE__*/ create.createMotionComponent("small"); +const MotionSource = /*@__PURE__*/ create.createMotionComponent("source"); +const MotionSpan = /*@__PURE__*/ create.createMotionComponent("span"); +const MotionStrong = /*@__PURE__*/ create.createMotionComponent("strong"); +const MotionStyle = /*@__PURE__*/ create.createMotionComponent("style"); +const MotionSub = /*@__PURE__*/ create.createMotionComponent("sub"); +const MotionSummary = /*@__PURE__*/ create.createMotionComponent("summary"); +const MotionSup = /*@__PURE__*/ create.createMotionComponent("sup"); +const MotionTable = /*@__PURE__*/ create.createMotionComponent("table"); +const MotionTbody = /*@__PURE__*/ create.createMotionComponent("tbody"); +const MotionTd = /*@__PURE__*/ create.createMotionComponent("td"); +const MotionTextarea = /*@__PURE__*/ create.createMotionComponent("textarea"); +const MotionTfoot = /*@__PURE__*/ create.createMotionComponent("tfoot"); +const MotionTh = /*@__PURE__*/ create.createMotionComponent("th"); +const MotionThead = /*@__PURE__*/ create.createMotionComponent("thead"); +const MotionTime = /*@__PURE__*/ create.createMotionComponent("time"); +const MotionTitle = /*@__PURE__*/ create.createMotionComponent("title"); +const MotionTr = /*@__PURE__*/ create.createMotionComponent("tr"); +const MotionTrack = /*@__PURE__*/ create.createMotionComponent("track"); +const MotionU = /*@__PURE__*/ create.createMotionComponent("u"); +const MotionUl = /*@__PURE__*/ create.createMotionComponent("ul"); +const MotionVideo = /*@__PURE__*/ create.createMotionComponent("video"); +const MotionWbr = /*@__PURE__*/ create.createMotionComponent("wbr"); +const MotionWebview = /*@__PURE__*/ create.createMotionComponent("webview"); +/** + * SVG components + */ +const MotionAnimate = /*@__PURE__*/ create.createMotionComponent("animate"); +const MotionCircle = /*@__PURE__*/ create.createMotionComponent("circle"); +const MotionDefs = /*@__PURE__*/ create.createMotionComponent("defs"); +const MotionDesc = /*@__PURE__*/ create.createMotionComponent("desc"); +const MotionEllipse = /*@__PURE__*/ create.createMotionComponent("ellipse"); +const MotionG = /*@__PURE__*/ create.createMotionComponent("g"); +const MotionImage = /*@__PURE__*/ create.createMotionComponent("image"); +const MotionLine = /*@__PURE__*/ create.createMotionComponent("line"); +const MotionFilter = /*@__PURE__*/ create.createMotionComponent("filter"); +const MotionMarker = /*@__PURE__*/ create.createMotionComponent("marker"); +const MotionMask = /*@__PURE__*/ create.createMotionComponent("mask"); +const MotionMetadata = /*@__PURE__*/ create.createMotionComponent("metadata"); +const MotionPath = /*@__PURE__*/ create.createMotionComponent("path"); +const MotionPattern = /*@__PURE__*/ create.createMotionComponent("pattern"); +const MotionPolygon = /*@__PURE__*/ create.createMotionComponent("polygon"); +const MotionPolyline = /*@__PURE__*/ create.createMotionComponent("polyline"); +const MotionRect = /*@__PURE__*/ create.createMotionComponent("rect"); +const MotionStop = /*@__PURE__*/ create.createMotionComponent("stop"); +const MotionSvg = /*@__PURE__*/ create.createMotionComponent("svg"); +const MotionSymbol = /*@__PURE__*/ create.createMotionComponent("symbol"); +const MotionText = /*@__PURE__*/ create.createMotionComponent("text"); +const MotionTspan = /*@__PURE__*/ create.createMotionComponent("tspan"); +const MotionUse = /*@__PURE__*/ create.createMotionComponent("use"); +const MotionView = /*@__PURE__*/ create.createMotionComponent("view"); +const MotionClipPath = /*@__PURE__*/ create.createMotionComponent("clipPath"); +const MotionFeBlend = /*@__PURE__*/ create.createMotionComponent("feBlend"); +const MotionFeColorMatrix = +/*@__PURE__*/ create.createMotionComponent("feColorMatrix"); +const MotionFeComponentTransfer = /*@__PURE__*/ create.createMotionComponent("feComponentTransfer"); +const MotionFeComposite = +/*@__PURE__*/ create.createMotionComponent("feComposite"); +const MotionFeConvolveMatrix = +/*@__PURE__*/ create.createMotionComponent("feConvolveMatrix"); +const MotionFeDiffuseLighting = +/*@__PURE__*/ create.createMotionComponent("feDiffuseLighting"); +const MotionFeDisplacementMap = +/*@__PURE__*/ create.createMotionComponent("feDisplacementMap"); +const MotionFeDistantLight = +/*@__PURE__*/ create.createMotionComponent("feDistantLight"); +const MotionFeDropShadow = +/*@__PURE__*/ create.createMotionComponent("feDropShadow"); +const MotionFeFlood = /*@__PURE__*/ create.createMotionComponent("feFlood"); +const MotionFeFuncA = /*@__PURE__*/ create.createMotionComponent("feFuncA"); +const MotionFeFuncB = /*@__PURE__*/ create.createMotionComponent("feFuncB"); +const MotionFeFuncG = /*@__PURE__*/ create.createMotionComponent("feFuncG"); +const MotionFeFuncR = /*@__PURE__*/ create.createMotionComponent("feFuncR"); +const MotionFeGaussianBlur = +/*@__PURE__*/ create.createMotionComponent("feGaussianBlur"); +const MotionFeImage = /*@__PURE__*/ create.createMotionComponent("feImage"); +const MotionFeMerge = /*@__PURE__*/ create.createMotionComponent("feMerge"); +const MotionFeMergeNode = +/*@__PURE__*/ create.createMotionComponent("feMergeNode"); +const MotionFeMorphology = +/*@__PURE__*/ create.createMotionComponent("feMorphology"); +const MotionFeOffset = /*@__PURE__*/ create.createMotionComponent("feOffset"); +const MotionFePointLight = +/*@__PURE__*/ create.createMotionComponent("fePointLight"); +const MotionFeSpecularLighting = +/*@__PURE__*/ create.createMotionComponent("feSpecularLighting"); +const MotionFeSpotLight = +/*@__PURE__*/ create.createMotionComponent("feSpotLight"); +const MotionFeTile = /*@__PURE__*/ create.createMotionComponent("feTile"); +const MotionFeTurbulence = +/*@__PURE__*/ create.createMotionComponent("feTurbulence"); +const MotionForeignObject = +/*@__PURE__*/ create.createMotionComponent("foreignObject"); +const MotionLinearGradient = +/*@__PURE__*/ create.createMotionComponent("linearGradient"); +const MotionRadialGradient = +/*@__PURE__*/ create.createMotionComponent("radialGradient"); +const MotionTextPath = /*@__PURE__*/ create.createMotionComponent("textPath"); + +exports.create = create.createMotionComponent; +exports.a = MotionA; +exports.abbr = MotionAbbr; +exports.address = MotionAddress; +exports.animate = MotionAnimate; +exports.area = MotionArea; +exports.article = MotionArticle; +exports.aside = MotionAside; +exports.audio = MotionAudio; +exports.b = MotionB; +exports.base = MotionBase; +exports.bdi = MotionBdi; +exports.bdo = MotionBdo; +exports.big = MotionBig; +exports.blockquote = MotionBlockquote; +exports.body = MotionBody; +exports.button = MotionButton; +exports.canvas = MotionCanvas; +exports.caption = MotionCaption; +exports.circle = MotionCircle; +exports.cite = MotionCite; +exports.clipPath = MotionClipPath; +exports.code = MotionCode; +exports.col = MotionCol; +exports.colgroup = MotionColgroup; +exports.data = MotionData; +exports.datalist = MotionDatalist; +exports.dd = MotionDd; +exports.defs = MotionDefs; +exports.del = MotionDel; +exports.desc = MotionDesc; +exports.details = MotionDetails; +exports.dfn = MotionDfn; +exports.dialog = MotionDialog; +exports.div = MotionDiv; +exports.dl = MotionDl; +exports.dt = MotionDt; +exports.ellipse = MotionEllipse; +exports.em = MotionEm; +exports.embed = MotionEmbed; +exports.feBlend = MotionFeBlend; +exports.feColorMatrix = MotionFeColorMatrix; +exports.feComponentTransfer = MotionFeComponentTransfer; +exports.feComposite = MotionFeComposite; +exports.feConvolveMatrix = MotionFeConvolveMatrix; +exports.feDiffuseLighting = MotionFeDiffuseLighting; +exports.feDisplacementMap = MotionFeDisplacementMap; +exports.feDistantLight = MotionFeDistantLight; +exports.feDropShadow = MotionFeDropShadow; +exports.feFlood = MotionFeFlood; +exports.feFuncA = MotionFeFuncA; +exports.feFuncB = MotionFeFuncB; +exports.feFuncG = MotionFeFuncG; +exports.feFuncR = MotionFeFuncR; +exports.feGaussianBlur = MotionFeGaussianBlur; +exports.feImage = MotionFeImage; +exports.feMerge = MotionFeMerge; +exports.feMergeNode = MotionFeMergeNode; +exports.feMorphology = MotionFeMorphology; +exports.feOffset = MotionFeOffset; +exports.fePointLight = MotionFePointLight; +exports.feSpecularLighting = MotionFeSpecularLighting; +exports.feSpotLight = MotionFeSpotLight; +exports.feTile = MotionFeTile; +exports.feTurbulence = MotionFeTurbulence; +exports.fieldset = MotionFieldset; +exports.figcaption = MotionFigcaption; +exports.figure = MotionFigure; +exports.filter = MotionFilter; +exports.footer = MotionFooter; +exports.foreignObject = MotionForeignObject; +exports.form = MotionForm; +exports.g = MotionG; +exports.h1 = MotionH1; +exports.h2 = MotionH2; +exports.h3 = MotionH3; +exports.h4 = MotionH4; +exports.h5 = MotionH5; +exports.h6 = MotionH6; +exports.head = MotionHead; +exports.header = MotionHeader; +exports.hgroup = MotionHgroup; +exports.hr = MotionHr; +exports.html = MotionHtml; +exports.i = MotionI; +exports.iframe = MotionIframe; +exports.image = MotionImage; +exports.img = MotionImg; +exports.input = MotionInput; +exports.ins = MotionIns; +exports.kbd = MotionKbd; +exports.keygen = MotionKeygen; +exports.label = MotionLabel; +exports.legend = MotionLegend; +exports.li = MotionLi; +exports.line = MotionLine; +exports.linearGradient = MotionLinearGradient; +exports.link = MotionLink; +exports.main = MotionMain; +exports.map = MotionMap; +exports.mark = MotionMark; +exports.marker = MotionMarker; +exports.mask = MotionMask; +exports.menu = MotionMenu; +exports.menuitem = MotionMenuitem; +exports.metadata = MotionMetadata; +exports.meter = MotionMeter; +exports.nav = MotionNav; +exports.object = MotionObject; +exports.ol = MotionOl; +exports.optgroup = MotionOptgroup; +exports.option = MotionOption; +exports.output = MotionOutput; +exports.p = MotionP; +exports.param = MotionParam; +exports.path = MotionPath; +exports.pattern = MotionPattern; +exports.picture = MotionPicture; +exports.polygon = MotionPolygon; +exports.polyline = MotionPolyline; +exports.pre = MotionPre; +exports.progress = MotionProgress; +exports.q = MotionQ; +exports.radialGradient = MotionRadialGradient; +exports.rect = MotionRect; +exports.rp = MotionRp; +exports.rt = MotionRt; +exports.ruby = MotionRuby; +exports.s = MotionS; +exports.samp = MotionSamp; +exports.script = MotionScript; +exports.section = MotionSection; +exports.select = MotionSelect; +exports.small = MotionSmall; +exports.source = MotionSource; +exports.span = MotionSpan; +exports.stop = MotionStop; +exports.strong = MotionStrong; +exports.style = MotionStyle; +exports.sub = MotionSub; +exports.summary = MotionSummary; +exports.sup = MotionSup; +exports.svg = MotionSvg; +exports.symbol = MotionSymbol; +exports.table = MotionTable; +exports.tbody = MotionTbody; +exports.td = MotionTd; +exports.text = MotionText; +exports.textPath = MotionTextPath; +exports.textarea = MotionTextarea; +exports.tfoot = MotionTfoot; +exports.th = MotionTh; +exports.thead = MotionThead; +exports.time = MotionTime; +exports.title = MotionTitle; +exports.tr = MotionTr; +exports.track = MotionTrack; +exports.tspan = MotionTspan; +exports.u = MotionU; +exports.ul = MotionUl; +exports.use = MotionUse; +exports.video = MotionVideo; +exports.view = MotionView; +exports.wbr = MotionWbr; +exports.webview = MotionWebview; diff --git a/node_modules/framer-motion/dist/cjs/create-C-c1JfhA.js b/node_modules/framer-motion/dist/cjs/create-C-c1JfhA.js new file mode 100644 index 00000000..12389674 --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/create-C-c1JfhA.js @@ -0,0 +1,6267 @@ +'use strict'; + +var motionDom = require('motion-dom'); +var motionUtils = require('motion-utils'); +var jsxRuntime = require('react/jsx-runtime'); +var React = require('react'); + +const LayoutGroupContext = React.createContext({}); + +/** + * Creates a constant value over the lifecycle of a component. + * + * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer + * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` + * you can ensure that initialisers don't execute twice or more. + */ +function useConstant(init) { + const ref = React.useRef(null); + if (ref.current === null) { + ref.current = init(); + } + return ref.current; +} + +const isBrowser = typeof window !== "undefined"; + +const useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect; + +/** + * @public + */ +const PresenceContext = +/* @__PURE__ */ React.createContext(null); + +/** + * @public + */ +const MotionConfigContext = React.createContext({ + transformPagePoint: (p) => p, + isStatic: false, + reducedMotion: "never", +}); + +/** + * When a component is the child of `AnimatePresence`, it can use `usePresence` + * to access information about whether it's still present in the React tree. + * + * ```jsx + * import { usePresence } from "framer-motion" + * + * export const Component = () => { + * const [isPresent, safeToRemove] = usePresence() + * + * useEffect(() => { + * !isPresent && setTimeout(safeToRemove, 1000) + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * If `isPresent` is `false`, it means that a component has been removed the tree, but + * `AnimatePresence` won't really remove it until `safeToRemove` has been called. + * + * @public + */ +function usePresence(subscribe = true) { + const context = React.useContext(PresenceContext); + if (context === null) + return [true, null]; + const { isPresent, onExitComplete, register } = context; + // It's safe to call the following hooks conditionally (after an early return) because the context will always + // either be null or non-null for the lifespan of the component. + const id = React.useId(); + React.useEffect(() => { + if (subscribe) { + return register(id); + } + }, [subscribe]); + const safeToRemove = React.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]); + return !isPresent && onExitComplete ? [false, safeToRemove] : [true]; +} +/** + * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present. + * There is no `safeToRemove` function. + * + * ```jsx + * import { useIsPresent } from "framer-motion" + * + * export const Component = () => { + * const isPresent = useIsPresent() + * + * useEffect(() => { + * !isPresent && console.log("I've been removed!") + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * @public + */ +function useIsPresent() { + return isPresent(React.useContext(PresenceContext)); +} +function isPresent(context) { + return context === null ? true : context.isPresent; +} + +const SCALE_PRECISION = 0.0001; +const SCALE_MIN = 1 - SCALE_PRECISION; +const SCALE_MAX = 1 + SCALE_PRECISION; +const TRANSLATE_PRECISION = 0.01; +const TRANSLATE_MIN = 0 - TRANSLATE_PRECISION; +const TRANSLATE_MAX = 0 + TRANSLATE_PRECISION; +function calcLength(axis) { + return axis.max - axis.min; +} +function isNear(value, target, maxDistance) { + return Math.abs(value - target) <= maxDistance; +} +function calcAxisDelta(delta, source, target, origin = 0.5) { + delta.origin = origin; + delta.originPoint = motionDom.mixNumber(source.min, source.max, delta.origin); + delta.scale = calcLength(target) / calcLength(source); + delta.translate = + motionDom.mixNumber(target.min, target.max, delta.origin) - delta.originPoint; + if ((delta.scale >= SCALE_MIN && delta.scale <= SCALE_MAX) || + isNaN(delta.scale)) { + delta.scale = 1.0; + } + if ((delta.translate >= TRANSLATE_MIN && + delta.translate <= TRANSLATE_MAX) || + isNaN(delta.translate)) { + delta.translate = 0.0; + } +} +function calcBoxDelta(delta, source, target, origin) { + calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined); + calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined); +} +function calcRelativeAxis(target, relative, parent) { + target.min = parent.min + relative.min; + target.max = target.min + calcLength(relative); +} +function calcRelativeBox(target, relative, parent) { + calcRelativeAxis(target.x, relative.x, parent.x); + calcRelativeAxis(target.y, relative.y, parent.y); +} +function calcRelativeAxisPosition(target, layout, parent) { + target.min = layout.min - parent.min; + target.max = target.min + calcLength(layout); +} +function calcRelativePosition(target, layout, parent) { + calcRelativeAxisPosition(target.x, layout.x, parent.x); + calcRelativeAxisPosition(target.y, layout.y, parent.y); +} + +const isNotNull = (value) => value !== null; +function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const index = repeat && repeatType !== "loop" && repeat % 2 === 1 + ? 0 + : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; +} + +const underDampedSpring = { + type: "spring", + stiffness: 500, + damping: 25, + restSpeed: 10, +}; +const criticallyDampedSpring = (target) => ({ + type: "spring", + stiffness: 550, + damping: target === 0 ? 2 * Math.sqrt(550) : 30, + restSpeed: 10, +}); +const keyframesTransition = { + type: "keyframes", + duration: 0.8, +}; +/** + * Default easing curve is a slightly shallower version of + * the default browser easing curve. + */ +const ease = { + type: "keyframes", + ease: [0.25, 0.1, 0.35, 1], + duration: 0.3, +}; +const getDefaultTransition = (valueKey, { keyframes }) => { + if (keyframes.length > 2) { + return keyframesTransition; + } + else if (motionDom.transformProps.has(valueKey)) { + return valueKey.startsWith("scale") + ? criticallyDampedSpring(keyframes[1]) + : underDampedSpring; + } + return ease; +}; + +/** + * Decide whether a transition is defined on a given Transition. + * This filters out orchestration options and returns true + * if any options are left. + */ +function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) { + return !!Object.keys(transition).length; +} + +const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => { + const valueTransition = motionDom.getValueTransition(transition, name) || {}; + /** + * Most transition values are currently completely overwritten by value-specific + * transitions. In the future it'd be nicer to blend these transitions. But for now + * delay actually does inherit from the root transition if not value-specific. + */ + const delay = valueTransition.delay || transition.delay || 0; + /** + * Elapsed isn't a public transition option but can be passed through from + * optimized appear effects in milliseconds. + */ + let { elapsed = 0 } = transition; + elapsed = elapsed - motionUtils.secondsToMilliseconds(delay); + const options = { + keyframes: Array.isArray(target) ? target : [null, target], + ease: "easeOut", + velocity: value.getVelocity(), + ...valueTransition, + delay: -elapsed, + onUpdate: (v) => { + value.set(v); + valueTransition.onUpdate && valueTransition.onUpdate(v); + }, + onComplete: () => { + onComplete(); + valueTransition.onComplete && valueTransition.onComplete(); + }, + name, + motionValue: value, + element: isHandoff ? undefined : element, + }; + /** + * If there's no transition defined for this value, we can generate + * unique transition settings for this value. + */ + if (!isTransitionDefined(valueTransition)) { + Object.assign(options, getDefaultTransition(name, options)); + } + /** + * Both WAAPI and our internal animation functions use durations + * as defined by milliseconds, while our external API defines them + * as seconds. + */ + options.duration && (options.duration = motionUtils.secondsToMilliseconds(options.duration)); + options.repeatDelay && (options.repeatDelay = motionUtils.secondsToMilliseconds(options.repeatDelay)); + /** + * Support deprecated way to set initial value. Prefer keyframe syntax. + */ + if (options.from !== undefined) { + options.keyframes[0] = options.from; + } + let shouldSkip = false; + if (options.type === false || + (options.duration === 0 && !options.repeatDelay)) { + options.duration = 0; + if (options.delay === 0) { + shouldSkip = true; + } + } + if (motionUtils.MotionGlobalConfig.instantAnimations || + motionUtils.MotionGlobalConfig.skipAnimations) { + shouldSkip = true; + options.duration = 0; + options.delay = 0; + } + /** + * If the transition type or easing has been explicitly set by the user + * then we don't want to allow flattening the animation. + */ + options.allowFlatten = !valueTransition.type && !valueTransition.ease; + /** + * If we can or must skip creating the animation, and apply only + * the final keyframe, do so. We also check once keyframes are resolved but + * this early check prevents the need to create an animation at all. + */ + if (shouldSkip && !isHandoff && value.get() !== undefined) { + const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition); + if (finalKeyframe !== undefined) { + motionDom.frame.update(() => { + options.onUpdate(finalKeyframe); + options.onComplete(); + }); + return; + } + } + return valueTransition.isSync + ? new motionDom.JSAnimation(options) + : new motionDom.AsyncMotionValueAnimation(options); +}; + +function animateSingleValue(value, keyframes, options) { + const motionValue = motionDom.isMotionValue(value) ? value : motionDom.motionValue(value); + motionValue.start(animateMotionValue("", motionValue, keyframes, options)); + return motionValue.animation; +} + +/** + * Convert camelCase to dash-case properties. + */ +const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase(); + +const optimizedAppearDataId = "framerAppearId"; +const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId); + +function getOptimisedAppearId(visualElement) { + return visualElement.props[optimizedAppearDataAttribute]; +} + +const compareByDepth = (a, b) => a.depth - b.depth; + +class FlatTree { + constructor() { + this.children = []; + this.isDirty = false; + } + add(child) { + motionUtils.addUniqueItem(this.children, child); + this.isDirty = true; + } + remove(child) { + motionUtils.removeItem(this.children, child); + this.isDirty = true; + } + forEach(callback) { + this.isDirty && this.children.sort(compareByDepth); + this.isDirty = false; + this.children.forEach(callback); + } +} + +/** + * Timeout defined in ms + */ +function delay(callback, timeout) { + const start = motionDom.time.now(); + const checkElapsed = ({ timestamp }) => { + const elapsed = timestamp - start; + if (elapsed >= timeout) { + motionDom.cancelFrame(checkElapsed); + callback(elapsed - timeout); + } + }; + motionDom.frame.setup(checkElapsed, true); + return () => motionDom.cancelFrame(checkElapsed); +} + +/** + * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself + * + * TODO: Remove and move to library + */ +function resolveMotionValue(value) { + return motionDom.isMotionValue(value) ? value.get() : value; +} + +const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"]; +const numBorders = borders.length; +const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value; +const isPx = (value) => typeof value === "number" || motionDom.px.test(value); +function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) { + if (shouldCrossfadeOpacity) { + target.opacity = motionDom.mixNumber(0, lead.opacity ?? 1, easeCrossfadeIn(progress)); + target.opacityExit = motionDom.mixNumber(follow.opacity ?? 1, 0, easeCrossfadeOut(progress)); + } + else if (isOnlyMember) { + target.opacity = motionDom.mixNumber(follow.opacity ?? 1, lead.opacity ?? 1, progress); + } + /** + * Mix border radius + */ + for (let i = 0; i < numBorders; i++) { + const borderLabel = `border${borders[i]}Radius`; + let followRadius = getRadius(follow, borderLabel); + let leadRadius = getRadius(lead, borderLabel); + if (followRadius === undefined && leadRadius === undefined) + continue; + followRadius || (followRadius = 0); + leadRadius || (leadRadius = 0); + const canMix = followRadius === 0 || + leadRadius === 0 || + isPx(followRadius) === isPx(leadRadius); + if (canMix) { + target[borderLabel] = Math.max(motionDom.mixNumber(asNumber(followRadius), asNumber(leadRadius), progress), 0); + if (motionDom.percent.test(leadRadius) || motionDom.percent.test(followRadius)) { + target[borderLabel] += "%"; + } + } + else { + target[borderLabel] = leadRadius; + } + } + /** + * Mix rotation + */ + if (follow.rotate || lead.rotate) { + target.rotate = motionDom.mixNumber(follow.rotate || 0, lead.rotate || 0, progress); + } +} +function getRadius(values, radiusName) { + return values[radiusName] !== undefined + ? values[radiusName] + : values.borderRadius; +} +// /** +// * We only want to mix the background color if there's a follow element +// * that we're not crossfading opacity between. For instance with switch +// * AnimateSharedLayout animations, this helps the illusion of a continuous +// * element being animated but also cuts down on the number of paints triggered +// * for elements where opacity is doing that work for us. +// */ +// if ( +// !hasFollowElement && +// latestLeadValues.backgroundColor && +// latestFollowValues.backgroundColor +// ) { +// /** +// * This isn't ideal performance-wise as mixColor is creating a new function every frame. +// * We could probably create a mixer that runs at the start of the animation but +// * the idea behind the crossfader is that it runs dynamically between two potentially +// * changing targets (ie opacity or borderRadius may be animating independently via variants) +// */ +// leadState.backgroundColor = followState.backgroundColor = mixColor( +// latestFollowValues.backgroundColor as string, +// latestLeadValues.backgroundColor as string +// )(p) +// } +const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, motionUtils.circOut); +const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, motionUtils.noop); +function compress(min, max, easing) { + return (p) => { + // Could replace ifs with clamp + if (p < min) + return 0; + if (p > max) + return 1; + return easing(motionUtils.progress(min, max, p)); + }; +} + +/** + * Reset an axis to the provided origin box. + * + * This is a mutative operation. + */ +function copyAxisInto(axis, originAxis) { + axis.min = originAxis.min; + axis.max = originAxis.max; +} +/** + * Reset a box to the provided origin box. + * + * This is a mutative operation. + */ +function copyBoxInto(box, originBox) { + copyAxisInto(box.x, originBox.x); + copyAxisInto(box.y, originBox.y); +} +/** + * Reset a delta to the provided origin box. + * + * This is a mutative operation. + */ +function copyAxisDeltaInto(delta, originDelta) { + delta.translate = originDelta.translate; + delta.scale = originDelta.scale; + delta.originPoint = originDelta.originPoint; + delta.origin = originDelta.origin; +} + +function isIdentityScale(scale) { + return scale === undefined || scale === 1; +} +function hasScale({ scale, scaleX, scaleY }) { + return (!isIdentityScale(scale) || + !isIdentityScale(scaleX) || + !isIdentityScale(scaleY)); +} +function hasTransform(values) { + return (hasScale(values) || + has2DTranslate(values) || + values.z || + values.rotate || + values.rotateX || + values.rotateY || + values.skewX || + values.skewY); +} +function has2DTranslate(values) { + return is2DTranslate(values.x) || is2DTranslate(values.y); +} +function is2DTranslate(value) { + return value && value !== "0%"; +} + +/** + * Scales a point based on a factor and an originPoint + */ +function scalePoint(point, scale, originPoint) { + const distanceFromOrigin = point - originPoint; + const scaled = scale * distanceFromOrigin; + return originPoint + scaled; +} +/** + * Applies a translate/scale delta to a point + */ +function applyPointDelta(point, translate, scale, originPoint, boxScale) { + if (boxScale !== undefined) { + point = scalePoint(point, boxScale, originPoint); + } + return scalePoint(point, scale, originPoint) + translate; +} +/** + * Applies a translate/scale delta to an axis + */ +function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) { + axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale); + axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale); +} +/** + * Applies a translate/scale delta to a box + */ +function applyBoxDelta(box, { x, y }) { + applyAxisDelta(box.x, x.translate, x.scale, x.originPoint); + applyAxisDelta(box.y, y.translate, y.scale, y.originPoint); +} +const TREE_SCALE_SNAP_MIN = 0.999999999999; +const TREE_SCALE_SNAP_MAX = 1.0000000000001; +/** + * Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms + * in a tree upon our box before then calculating how to project it into our desired viewport-relative box + * + * This is the final nested loop within updateLayoutDelta for future refactoring + */ +function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) { + const treeLength = treePath.length; + if (!treeLength) + return; + // Reset the treeScale + treeScale.x = treeScale.y = 1; + let node; + let delta; + for (let i = 0; i < treeLength; i++) { + node = treePath[i]; + delta = node.projectionDelta; + /** + * TODO: Prefer to remove this, but currently we have motion components with + * display: contents in Framer. + */ + const { visualElement } = node.options; + if (visualElement && + visualElement.props.style && + visualElement.props.style.display === "contents") { + continue; + } + if (isSharedTransition && + node.options.layoutScroll && + node.scroll && + node !== node.root) { + transformBox(box, { + x: -node.scroll.offset.x, + y: -node.scroll.offset.y, + }); + } + if (delta) { + // Incoporate each ancestor's scale into a culmulative treeScale for this component + treeScale.x *= delta.x.scale; + treeScale.y *= delta.y.scale; + // Apply each ancestor's calculated delta into this component's recorded layout box + applyBoxDelta(box, delta); + } + if (isSharedTransition && hasTransform(node.latestValues)) { + transformBox(box, node.latestValues); + } + } + /** + * Snap tree scale back to 1 if it's within a non-perceivable threshold. + * This will help reduce useless scales getting rendered. + */ + if (treeScale.x < TREE_SCALE_SNAP_MAX && + treeScale.x > TREE_SCALE_SNAP_MIN) { + treeScale.x = 1.0; + } + if (treeScale.y < TREE_SCALE_SNAP_MAX && + treeScale.y > TREE_SCALE_SNAP_MIN) { + treeScale.y = 1.0; + } +} +function translateAxis(axis, distance) { + axis.min = axis.min + distance; + axis.max = axis.max + distance; +} +/** + * Apply a transform to an axis from the latest resolved motion values. + * This function basically acts as a bridge between a flat motion value map + * and applyAxisDelta + */ +function transformAxis(axis, axisTranslate, axisScale, boxScale, axisOrigin = 0.5) { + const originPoint = motionDom.mixNumber(axis.min, axis.max, axisOrigin); + // Apply the axis delta to the final axis + applyAxisDelta(axis, axisTranslate, axisScale, originPoint, boxScale); +} +/** + * Apply a transform to a box from the latest resolved motion values. + */ +function transformBox(box, transform) { + transformAxis(box.x, transform.x, transform.scaleX, transform.scale, transform.originX); + transformAxis(box.y, transform.y, transform.scaleY, transform.scale, transform.originY); +} + +/** + * Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse + */ +function removePointDelta(point, translate, scale, originPoint, boxScale) { + point -= translate; + point = scalePoint(point, 1 / scale, originPoint); + if (boxScale !== undefined) { + point = scalePoint(point, 1 / boxScale, originPoint); + } + return point; +} +/** + * Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse + */ +function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) { + if (motionDom.percent.test(translate)) { + translate = parseFloat(translate); + const relativeProgress = motionDom.mixNumber(sourceAxis.min, sourceAxis.max, translate / 100); + translate = relativeProgress - sourceAxis.min; + } + if (typeof translate !== "number") + return; + let originPoint = motionDom.mixNumber(originAxis.min, originAxis.max, origin); + if (axis === originAxis) + originPoint -= translate; + axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale); + axis.max = removePointDelta(axis.max, translate, scale, originPoint, boxScale); +} +/** + * Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse + * and acts as a bridge between motion values and removeAxisDelta + */ +function removeAxisTransforms(axis, transforms, [key, scaleKey, originKey], origin, sourceAxis) { + removeAxisDelta(axis, transforms[key], transforms[scaleKey], transforms[originKey], transforms.scale, origin, sourceAxis); +} +/** + * The names of the motion values we want to apply as translation, scale and origin. + */ +const xKeys = ["x", "scaleX", "originX"]; +const yKeys = ["y", "scaleY", "originY"]; +/** + * Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse + * and acts as a bridge between motion values and removeAxisDelta + */ +function removeBoxTransforms(box, transforms, originBox, sourceBox) { + removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined); + removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined); +} + +const createAxisDelta = () => ({ + translate: 0, + scale: 1, + origin: 0, + originPoint: 0, +}); +const createDelta = () => ({ + x: createAxisDelta(), + y: createAxisDelta(), +}); +const createAxis = () => ({ min: 0, max: 0 }); +const createBox = () => ({ + x: createAxis(), + y: createAxis(), +}); + +function isAxisDeltaZero(delta) { + return delta.translate === 0 && delta.scale === 1; +} +function isDeltaZero(delta) { + return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y); +} +function axisEquals(a, b) { + return a.min === b.min && a.max === b.max; +} +function boxEquals(a, b) { + return axisEquals(a.x, b.x) && axisEquals(a.y, b.y); +} +function axisEqualsRounded(a, b) { + return (Math.round(a.min) === Math.round(b.min) && + Math.round(a.max) === Math.round(b.max)); +} +function boxEqualsRounded(a, b) { + return axisEqualsRounded(a.x, b.x) && axisEqualsRounded(a.y, b.y); +} +function aspectRatio(box) { + return calcLength(box.x) / calcLength(box.y); +} +function axisDeltaEquals(a, b) { + return (a.translate === b.translate && + a.scale === b.scale && + a.originPoint === b.originPoint); +} + +class NodeStack { + constructor() { + this.members = []; + } + add(node) { + motionUtils.addUniqueItem(this.members, node); + node.scheduleRender(); + } + remove(node) { + motionUtils.removeItem(this.members, node); + if (node === this.prevLead) { + this.prevLead = undefined; + } + if (node === this.lead) { + const prevLead = this.members[this.members.length - 1]; + if (prevLead) { + this.promote(prevLead); + } + } + } + relegate(node) { + const indexOfNode = this.members.findIndex((member) => node === member); + if (indexOfNode === 0) + return false; + /** + * Find the next projection node that is present + */ + let prevLead; + for (let i = indexOfNode; i >= 0; i--) { + const member = this.members[i]; + if (member.isPresent !== false) { + prevLead = member; + break; + } + } + if (prevLead) { + this.promote(prevLead); + return true; + } + else { + return false; + } + } + promote(node, preserveFollowOpacity) { + const prevLead = this.lead; + if (node === prevLead) + return; + this.prevLead = prevLead; + this.lead = node; + node.show(); + if (prevLead) { + prevLead.instance && prevLead.scheduleRender(); + node.scheduleRender(); + node.resumeFrom = prevLead; + if (preserveFollowOpacity) { + node.resumeFrom.preserveOpacity = true; + } + if (prevLead.snapshot) { + node.snapshot = prevLead.snapshot; + node.snapshot.latestValues = + prevLead.animationValues || prevLead.latestValues; + } + if (node.root && node.root.isUpdating) { + node.isLayoutDirty = true; + } + const { crossfade } = node.options; + if (crossfade === false) { + prevLead.hide(); + } + /** + * TODO: + * - Test border radius when previous node was deleted + * - boxShadow mixing + * - Shared between element A in scrolled container and element B (scroll stays the same or changes) + * - Shared between element A in transformed container and element B (transform stays the same or changes) + * - Shared between element A in scrolled page and element B (scroll stays the same or changes) + * --- + * - Crossfade opacity of root nodes + * - layoutId changes after animation + * - layoutId changes mid animation + */ + } + } + exitAnimationComplete() { + this.members.forEach((node) => { + const { options, resumingFrom } = node; + options.onExitComplete && options.onExitComplete(); + if (resumingFrom) { + resumingFrom.options.onExitComplete && + resumingFrom.options.onExitComplete(); + } + }); + } + scheduleRender() { + this.members.forEach((node) => { + node.instance && node.scheduleRender(false); + }); + } + /** + * Clear any leads that have been removed this render to prevent them from being + * used in future animations and to prevent memory leaks + */ + removeLeadSnapshot() { + if (this.lead && this.lead.snapshot) { + this.lead.snapshot = undefined; + } + } +} + +const scaleCorrectors = {}; +function addScaleCorrector(correctors) { + for (const key in correctors) { + scaleCorrectors[key] = correctors[key]; + if (motionDom.isCSSVariableName(key)) { + scaleCorrectors[key].isCSSVariable = true; + } + } +} + +function buildProjectionTransform(delta, treeScale, latestTransform) { + let transform = ""; + /** + * The translations we use to calculate are always relative to the viewport coordinate space. + * But when we apply scales, we also scale the coordinate space of an element and its children. + * For instance if we have a treeScale (the culmination of all parent scales) of 0.5 and we need + * to move an element 100 pixels, we actually need to move it 200 in within that scaled space. + */ + const xTranslate = delta.x.translate / treeScale.x; + const yTranslate = delta.y.translate / treeScale.y; + const zTranslate = latestTransform?.z || 0; + if (xTranslate || yTranslate || zTranslate) { + transform = `translate3d(${xTranslate}px, ${yTranslate}px, ${zTranslate}px) `; + } + /** + * Apply scale correction for the tree transform. + * This will apply scale to the screen-orientated axes. + */ + if (treeScale.x !== 1 || treeScale.y !== 1) { + transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `; + } + if (latestTransform) { + const { transformPerspective, rotate, rotateX, rotateY, skewX, skewY } = latestTransform; + if (transformPerspective) + transform = `perspective(${transformPerspective}px) ${transform}`; + if (rotate) + transform += `rotate(${rotate}deg) `; + if (rotateX) + transform += `rotateX(${rotateX}deg) `; + if (rotateY) + transform += `rotateY(${rotateY}deg) `; + if (skewX) + transform += `skewX(${skewX}deg) `; + if (skewY) + transform += `skewY(${skewY}deg) `; + } + /** + * Apply scale to match the size of the element to the size we want it. + * This will apply scale to the element-orientated axes. + */ + const elementScaleX = delta.x.scale * treeScale.x; + const elementScaleY = delta.y.scale * treeScale.y; + if (elementScaleX !== 1 || elementScaleY !== 1) { + transform += `scale(${elementScaleX}, ${elementScaleY})`; + } + return transform || "none"; +} + +function eachAxis(callback) { + return [callback("x"), callback("y")]; +} + +/** + * This should only ever be modified on the client otherwise it'll + * persist through server requests. If we need instanced states we + * could lazy-init via root. + */ +const globalProjectionState = { + /** + * Global flag as to whether the tree has animated since the last time + * we resized the window + */ + hasAnimatedSinceResize: true, + /** + * We set this to true once, on the first update. Any nodes added to the tree beyond that + * update will be given a `data-projection-id` attribute. + */ + hasEverUpdated: false, +}; + +const metrics = { + nodes: 0, + calculatedTargetDeltas: 0, + calculatedProjections: 0, +}; +const transformAxes = ["", "X", "Y", "Z"]; +/** + * We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1 + * which has a noticeable difference in spring animations + */ +const animationTarget = 1000; +let id$1 = 0; +function resetDistortingTransform(key, visualElement, values, sharedAnimationValues) { + const { latestValues } = visualElement; + // Record the distorting transform and then temporarily set it to 0 + if (latestValues[key]) { + values[key] = latestValues[key]; + visualElement.setStaticValue(key, 0); + if (sharedAnimationValues) { + sharedAnimationValues[key] = 0; + } + } +} +function cancelTreeOptimisedTransformAnimations(projectionNode) { + projectionNode.hasCheckedOptimisedAppear = true; + if (projectionNode.root === projectionNode) + return; + const { visualElement } = projectionNode.options; + if (!visualElement) + return; + const appearId = getOptimisedAppearId(visualElement); + if (window.MotionHasOptimisedAnimation(appearId, "transform")) { + const { layout, layoutId } = projectionNode.options; + window.MotionCancelOptimisedAnimation(appearId, "transform", motionDom.frame, !(layout || layoutId)); + } + const { parent } = projectionNode; + if (parent && !parent.hasCheckedOptimisedAppear) { + cancelTreeOptimisedTransformAnimations(parent); + } +} +function createProjectionNode$1({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) { + return class ProjectionNode { + constructor(latestValues = {}, parent = defaultParent?.()) { + /** + * A unique ID generated for every projection node. + */ + this.id = id$1++; + /** + * An id that represents a unique session instigated by startUpdate. + */ + this.animationId = 0; + this.animationCommitId = 0; + /** + * A Set containing all this component's children. This is used to iterate + * through the children. + * + * TODO: This could be faster to iterate as a flat array stored on the root node. + */ + this.children = new Set(); + /** + * Options for the node. We use this to configure what kind of layout animations + * we should perform (if any). + */ + this.options = {}; + /** + * We use this to detect when its safe to shut down part of a projection tree. + * We have to keep projecting children for scale correction and relative projection + * until all their parents stop performing layout animations. + */ + this.isTreeAnimating = false; + this.isAnimationBlocked = false; + /** + * Flag to true if we think this layout has been changed. We can't always know this, + * currently we set it to true every time a component renders, or if it has a layoutDependency + * if that has changed between renders. Additionally, components can be grouped by LayoutGroup + * and if one node is dirtied, they all are. + */ + this.isLayoutDirty = false; + /** + * Flag to true if we think the projection calculations for this node needs + * recalculating as a result of an updated transform or layout animation. + */ + this.isProjectionDirty = false; + /** + * Flag to true if the layout *or* transform has changed. This then gets propagated + * throughout the projection tree, forcing any element below to recalculate on the next frame. + */ + this.isSharedProjectionDirty = false; + /** + * Flag transform dirty. This gets propagated throughout the whole tree but is only + * respected by shared nodes. + */ + this.isTransformDirty = false; + /** + * Block layout updates for instant layout transitions throughout the tree. + */ + this.updateManuallyBlocked = false; + this.updateBlockedByResize = false; + /** + * Set to true between the start of the first `willUpdate` call and the end of the `didUpdate` + * call. + */ + this.isUpdating = false; + /** + * If this is an SVG element we currently disable projection transforms + */ + this.isSVG = false; + /** + * Flag to true (during promotion) if a node doing an instant layout transition needs to reset + * its projection styles. + */ + this.needsReset = false; + /** + * Flags whether this node should have its transform reset prior to measuring. + */ + this.shouldResetTransform = false; + /** + * Store whether this node has been checked for optimised appear animations. As + * effects fire bottom-up, and we want to look up the tree for appear animations, + * this makes sure we only check each path once, stopping at nodes that + * have already been checked. + */ + this.hasCheckedOptimisedAppear = false; + /** + * An object representing the calculated contextual/accumulated/tree scale. + * This will be used to scale calculcated projection transforms, as these are + * calculated in screen-space but need to be scaled for elements to layoutly + * make it to their calculated destinations. + * + * TODO: Lazy-init + */ + this.treeScale = { x: 1, y: 1 }; + /** + * + */ + this.eventHandlers = new Map(); + this.hasTreeAnimated = false; + // Note: Currently only running on root node + this.updateScheduled = false; + this.scheduleUpdate = () => this.update(); + this.projectionUpdateScheduled = false; + this.checkUpdateFailed = () => { + if (this.isUpdating) { + this.isUpdating = false; + this.clearAllSnapshots(); + } + }; + /** + * This is a multi-step process as shared nodes might be of different depths. Nodes + * are sorted by depth order, so we need to resolve the entire tree before moving to + * the next step. + */ + this.updateProjection = () => { + this.projectionUpdateScheduled = false; + /** + * Reset debug counts. Manually resetting rather than creating a new + * object each frame. + */ + if (motionDom.statsBuffer.value) { + metrics.nodes = + metrics.calculatedTargetDeltas = + metrics.calculatedProjections = + 0; + } + this.nodes.forEach(propagateDirtyNodes); + this.nodes.forEach(resolveTargetDelta); + this.nodes.forEach(calcProjection); + this.nodes.forEach(cleanDirtyNodes); + if (motionDom.statsBuffer.addProjectionMetrics) { + motionDom.statsBuffer.addProjectionMetrics(metrics); + } + }; + /** + * Frame calculations + */ + this.resolvedRelativeTargetAt = 0.0; + this.hasProjected = false; + this.isVisible = true; + this.animationProgress = 0; + /** + * Shared layout + */ + // TODO Only running on root node + this.sharedNodes = new Map(); + this.latestValues = latestValues; + this.root = parent ? parent.root || parent : this; + this.path = parent ? [...parent.path, parent] : []; + this.parent = parent; + this.depth = parent ? parent.depth + 1 : 0; + for (let i = 0; i < this.path.length; i++) { + this.path[i].shouldResetTransform = true; + } + if (this.root === this) + this.nodes = new FlatTree(); + } + addEventListener(name, handler) { + if (!this.eventHandlers.has(name)) { + this.eventHandlers.set(name, new motionUtils.SubscriptionManager()); + } + return this.eventHandlers.get(name).add(handler); + } + notifyListeners(name, ...args) { + const subscriptionManager = this.eventHandlers.get(name); + subscriptionManager && subscriptionManager.notify(...args); + } + hasListeners(name) { + return this.eventHandlers.has(name); + } + /** + * Lifecycles + */ + mount(instance) { + if (this.instance) + return; + this.isSVG = motionDom.isSVGElement(instance) && !motionDom.isSVGSVGElement(instance); + this.instance = instance; + const { layoutId, layout, visualElement } = this.options; + if (visualElement && !visualElement.current) { + visualElement.mount(instance); + } + this.root.nodes.add(this); + this.parent && this.parent.children.add(this); + if (this.root.hasTreeAnimated && (layout || layoutId)) { + this.isLayoutDirty = true; + } + if (attachResizeListener) { + let cancelDelay; + let innerWidth = 0; + const resizeUnblockUpdate = () => (this.root.updateBlockedByResize = false); + // Set initial innerWidth in a frame.read callback to batch the read + motionDom.frame.read(() => { + innerWidth = window.innerWidth; + }); + attachResizeListener(instance, () => { + const newInnerWidth = window.innerWidth; + if (newInnerWidth === innerWidth) + return; + innerWidth = newInnerWidth; + this.root.updateBlockedByResize = true; + cancelDelay && cancelDelay(); + cancelDelay = delay(resizeUnblockUpdate, 250); + if (globalProjectionState.hasAnimatedSinceResize) { + globalProjectionState.hasAnimatedSinceResize = false; + this.nodes.forEach(finishAnimation); + } + }); + } + if (layoutId) { + this.root.registerSharedNode(layoutId, this); + } + // Only register the handler if it requires layout animation + if (this.options.animate !== false && + visualElement && + (layoutId || layout)) { + this.addEventListener("didUpdate", ({ delta, hasLayoutChanged, hasRelativeLayoutChanged, layout: newLayout, }) => { + if (this.isTreeAnimationBlocked()) { + this.target = undefined; + this.relativeTarget = undefined; + return; + } + // TODO: Check here if an animation exists + const layoutTransition = this.options.transition || + visualElement.getDefaultTransition() || + defaultLayoutTransition; + const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps(); + /** + * The target layout of the element might stay the same, + * but its position relative to its parent has changed. + */ + const hasTargetChanged = !this.targetLayout || + !boxEqualsRounded(this.targetLayout, newLayout); + /* + * Note: Disabled to fix relative animations always triggering new + * layout animations. If this causes further issues, we can try + * a different approach to detecting relative target changes. + */ + // || hasRelativeLayoutChanged + /** + * If the layout hasn't seemed to have changed, it might be that the + * element is visually in the same place in the document but its position + * relative to its parent has indeed changed. So here we check for that. + */ + const hasOnlyRelativeTargetChanged = !hasLayoutChanged && hasRelativeLayoutChanged; + if (this.options.layoutRoot || + this.resumeFrom || + hasOnlyRelativeTargetChanged || + (hasLayoutChanged && + (hasTargetChanged || !this.currentAnimation))) { + if (this.resumeFrom) { + this.resumingFrom = this.resumeFrom; + this.resumingFrom.resumingFrom = undefined; + } + const animationOptions = { + ...motionDom.getValueTransition(layoutTransition, "layout"), + onPlay: onLayoutAnimationStart, + onComplete: onLayoutAnimationComplete, + }; + if (visualElement.shouldReduceMotion || + this.options.layoutRoot) { + animationOptions.delay = 0; + animationOptions.type = false; + } + this.startAnimation(animationOptions); + /** + * Set animation origin after starting animation to avoid layout jump + * caused by stopping previous layout animation + */ + this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged); + } + else { + /** + * If the layout hasn't changed and we have an animation that hasn't started yet, + * finish it immediately. Otherwise it will be animating from a location + * that was probably never commited to screen and look like a jumpy box. + */ + if (!hasLayoutChanged) { + finishAnimation(this); + } + if (this.isLead() && this.options.onExitComplete) { + this.options.onExitComplete(); + } + } + this.targetLayout = newLayout; + }); + } + } + unmount() { + this.options.layoutId && this.willUpdate(); + this.root.nodes.remove(this); + const stack = this.getStack(); + stack && stack.remove(this); + this.parent && this.parent.children.delete(this); + this.instance = undefined; + this.eventHandlers.clear(); + motionDom.cancelFrame(this.updateProjection); + } + // only on the root + blockUpdate() { + this.updateManuallyBlocked = true; + } + unblockUpdate() { + this.updateManuallyBlocked = false; + } + isUpdateBlocked() { + return this.updateManuallyBlocked || this.updateBlockedByResize; + } + isTreeAnimationBlocked() { + return (this.isAnimationBlocked || + (this.parent && this.parent.isTreeAnimationBlocked()) || + false); + } + // Note: currently only running on root node + startUpdate() { + if (this.isUpdateBlocked()) + return; + this.isUpdating = true; + this.nodes && this.nodes.forEach(resetSkewAndRotation); + this.animationId++; + } + getTransformTemplate() { + const { visualElement } = this.options; + return visualElement && visualElement.getProps().transformTemplate; + } + willUpdate(shouldNotifyListeners = true) { + this.root.hasTreeAnimated = true; + if (this.root.isUpdateBlocked()) { + this.options.onExitComplete && this.options.onExitComplete(); + return; + } + /** + * If we're running optimised appear animations then these must be + * cancelled before measuring the DOM. This is so we can measure + * the true layout of the element rather than the WAAPI animation + * which will be unaffected by the resetSkewAndRotate step. + * + * Note: This is a DOM write. Worst case scenario is this is sandwiched + * between other snapshot reads which will cause unnecessary style recalculations. + * This has to happen here though, as we don't yet know which nodes will need + * snapshots in startUpdate(), but we only want to cancel optimised animations + * if a layout animation measurement is actually going to be affected by them. + */ + if (window.MotionCancelOptimisedAnimation && + !this.hasCheckedOptimisedAppear) { + cancelTreeOptimisedTransformAnimations(this); + } + !this.root.isUpdating && this.root.startUpdate(); + if (this.isLayoutDirty) + return; + this.isLayoutDirty = true; + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + node.shouldResetTransform = true; + node.updateScroll("snapshot"); + if (node.options.layoutRoot) { + node.willUpdate(false); + } + } + const { layoutId, layout } = this.options; + if (layoutId === undefined && !layout) + return; + const transformTemplate = this.getTransformTemplate(); + this.prevTransformTemplateValue = transformTemplate + ? transformTemplate(this.latestValues, "") + : undefined; + this.updateSnapshot(); + shouldNotifyListeners && this.notifyListeners("willUpdate"); + } + update() { + this.updateScheduled = false; + const updateWasBlocked = this.isUpdateBlocked(); + // When doing an instant transition, we skip the layout update, + // but should still clean up the measurements so that the next + // snapshot could be taken correctly. + if (updateWasBlocked) { + this.unblockUpdate(); + this.clearAllSnapshots(); + this.nodes.forEach(clearMeasurements); + return; + } + /** + * If this is a repeat of didUpdate then ignore the animation. + */ + if (this.animationId <= this.animationCommitId) { + this.nodes.forEach(clearIsLayoutDirty); + return; + } + this.animationCommitId = this.animationId; + if (!this.isUpdating) { + this.nodes.forEach(clearIsLayoutDirty); + } + else { + this.isUpdating = false; + /** + * Write + */ + this.nodes.forEach(resetTransformStyle); + /** + * Read ================== + */ + // Update layout measurements of updated children + this.nodes.forEach(updateLayout); + /** + * Write + */ + // Notify listeners that the layout is updated + this.nodes.forEach(notifyLayoutUpdate); + } + this.clearAllSnapshots(); + /** + * Manually flush any pending updates. Ideally + * we could leave this to the following requestAnimationFrame but this seems + * to leave a flash of incorrectly styled content. + */ + const now = motionDom.time.now(); + motionDom.frameData.delta = motionUtils.clamp(0, 1000 / 60, now - motionDom.frameData.timestamp); + motionDom.frameData.timestamp = now; + motionDom.frameData.isProcessing = true; + motionDom.frameSteps.update.process(motionDom.frameData); + motionDom.frameSteps.preRender.process(motionDom.frameData); + motionDom.frameSteps.render.process(motionDom.frameData); + motionDom.frameData.isProcessing = false; + } + didUpdate() { + if (!this.updateScheduled) { + this.updateScheduled = true; + motionDom.microtask.read(this.scheduleUpdate); + } + } + clearAllSnapshots() { + this.nodes.forEach(clearSnapshot); + this.sharedNodes.forEach(removeLeadSnapshots); + } + scheduleUpdateProjection() { + if (!this.projectionUpdateScheduled) { + this.projectionUpdateScheduled = true; + motionDom.frame.preRender(this.updateProjection, false, true); + } + } + scheduleCheckAfterUnmount() { + /** + * If the unmounting node is in a layoutGroup and did trigger a willUpdate, + * we manually call didUpdate to give a chance to the siblings to animate. + * Otherwise, cleanup all snapshots to prevents future nodes from reusing them. + */ + motionDom.frame.postRender(() => { + if (this.isLayoutDirty) { + this.root.didUpdate(); + } + else { + this.root.checkUpdateFailed(); + } + }); + } + /** + * Update measurements + */ + updateSnapshot() { + if (this.snapshot || !this.instance) + return; + this.snapshot = this.measure(); + if (this.snapshot && + !calcLength(this.snapshot.measuredBox.x) && + !calcLength(this.snapshot.measuredBox.y)) { + this.snapshot = undefined; + } + } + updateLayout() { + if (!this.instance) + return; + this.updateScroll(); + if (!(this.options.alwaysMeasureLayout && this.isLead()) && + !this.isLayoutDirty) { + return; + } + /** + * When a node is mounted, it simply resumes from the prevLead's + * snapshot instead of taking a new one, but the ancestors scroll + * might have updated while the prevLead is unmounted. We need to + * update the scroll again to make sure the layout we measure is + * up to date. + */ + if (this.resumeFrom && !this.resumeFrom.instance) { + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + node.updateScroll(); + } + } + const prevLayout = this.layout; + this.layout = this.measure(false); + this.layoutCorrected = createBox(); + this.isLayoutDirty = false; + this.projectionDelta = undefined; + this.notifyListeners("measure", this.layout.layoutBox); + const { visualElement } = this.options; + visualElement && + visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined); + } + updateScroll(phase = "measure") { + let needsMeasurement = Boolean(this.options.layoutScroll && this.instance); + if (this.scroll && + this.scroll.animationId === this.root.animationId && + this.scroll.phase === phase) { + needsMeasurement = false; + } + if (needsMeasurement && this.instance) { + const isRoot = checkIsScrollRoot(this.instance); + this.scroll = { + animationId: this.root.animationId, + phase, + isRoot, + offset: measureScroll(this.instance), + wasRoot: this.scroll ? this.scroll.isRoot : isRoot, + }; + } + } + resetTransform() { + if (!resetTransform) + return; + const isResetRequested = this.isLayoutDirty || + this.shouldResetTransform || + this.options.alwaysMeasureLayout; + const hasProjection = this.projectionDelta && !isDeltaZero(this.projectionDelta); + const transformTemplate = this.getTransformTemplate(); + const transformTemplateValue = transformTemplate + ? transformTemplate(this.latestValues, "") + : undefined; + const transformTemplateHasChanged = transformTemplateValue !== this.prevTransformTemplateValue; + if (isResetRequested && + this.instance && + (hasProjection || + hasTransform(this.latestValues) || + transformTemplateHasChanged)) { + resetTransform(this.instance, transformTemplateValue); + this.shouldResetTransform = false; + this.scheduleRender(); + } + } + measure(removeTransform = true) { + const pageBox = this.measurePageBox(); + let layoutBox = this.removeElementScroll(pageBox); + /** + * Measurements taken during the pre-render stage + * still have transforms applied so we remove them + * via calculation. + */ + if (removeTransform) { + layoutBox = this.removeTransform(layoutBox); + } + roundBox(layoutBox); + return { + animationId: this.root.animationId, + measuredBox: pageBox, + layoutBox, + latestValues: {}, + source: this.id, + }; + } + measurePageBox() { + const { visualElement } = this.options; + if (!visualElement) + return createBox(); + const box = visualElement.measureViewportBox(); + const wasInScrollRoot = this.scroll?.wasRoot || this.path.some(checkNodeWasScrollRoot); + if (!wasInScrollRoot) { + // Remove viewport scroll to give page-relative coordinates + const { scroll } = this.root; + if (scroll) { + translateAxis(box.x, scroll.offset.x); + translateAxis(box.y, scroll.offset.y); + } + } + return box; + } + removeElementScroll(box) { + const boxWithoutScroll = createBox(); + copyBoxInto(boxWithoutScroll, box); + if (this.scroll?.wasRoot) { + return boxWithoutScroll; + } + /** + * Performance TODO: Keep a cumulative scroll offset down the tree + * rather than loop back up the path. + */ + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + const { scroll, options } = node; + if (node !== this.root && scroll && options.layoutScroll) { + /** + * If this is a new scroll root, we want to remove all previous scrolls + * from the viewport box. + */ + if (scroll.wasRoot) { + copyBoxInto(boxWithoutScroll, box); + } + translateAxis(boxWithoutScroll.x, scroll.offset.x); + translateAxis(boxWithoutScroll.y, scroll.offset.y); + } + } + return boxWithoutScroll; + } + applyTransform(box, transformOnly = false) { + const withTransforms = createBox(); + copyBoxInto(withTransforms, box); + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + if (!transformOnly && + node.options.layoutScroll && + node.scroll && + node !== node.root) { + transformBox(withTransforms, { + x: -node.scroll.offset.x, + y: -node.scroll.offset.y, + }); + } + if (!hasTransform(node.latestValues)) + continue; + transformBox(withTransforms, node.latestValues); + } + if (hasTransform(this.latestValues)) { + transformBox(withTransforms, this.latestValues); + } + return withTransforms; + } + removeTransform(box) { + const boxWithoutTransform = createBox(); + copyBoxInto(boxWithoutTransform, box); + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + if (!node.instance) + continue; + if (!hasTransform(node.latestValues)) + continue; + hasScale(node.latestValues) && node.updateSnapshot(); + const sourceBox = createBox(); + const nodeBox = node.measurePageBox(); + copyBoxInto(sourceBox, nodeBox); + removeBoxTransforms(boxWithoutTransform, node.latestValues, node.snapshot ? node.snapshot.layoutBox : undefined, sourceBox); + } + if (hasTransform(this.latestValues)) { + removeBoxTransforms(boxWithoutTransform, this.latestValues); + } + return boxWithoutTransform; + } + setTargetDelta(delta) { + this.targetDelta = delta; + this.root.scheduleUpdateProjection(); + this.isProjectionDirty = true; + } + setOptions(options) { + this.options = { + ...this.options, + ...options, + crossfade: options.crossfade !== undefined ? options.crossfade : true, + }; + } + clearMeasurements() { + this.scroll = undefined; + this.layout = undefined; + this.snapshot = undefined; + this.prevTransformTemplateValue = undefined; + this.targetDelta = undefined; + this.target = undefined; + this.isLayoutDirty = false; + } + forceRelativeParentToResolveTarget() { + if (!this.relativeParent) + return; + /** + * If the parent target isn't up-to-date, force it to update. + * This is an unfortunate de-optimisation as it means any updating relative + * projection will cause all the relative parents to recalculate back + * up the tree. + */ + if (this.relativeParent.resolvedRelativeTargetAt !== + motionDom.frameData.timestamp) { + this.relativeParent.resolveTargetDelta(true); + } + } + resolveTargetDelta(forceRecalculation = false) { + /** + * Once the dirty status of nodes has been spread through the tree, we also + * need to check if we have a shared node of a different depth that has itself + * been dirtied. + */ + const lead = this.getLead(); + this.isProjectionDirty || (this.isProjectionDirty = lead.isProjectionDirty); + this.isTransformDirty || (this.isTransformDirty = lead.isTransformDirty); + this.isSharedProjectionDirty || (this.isSharedProjectionDirty = lead.isSharedProjectionDirty); + const isShared = Boolean(this.resumingFrom) || this !== lead; + /** + * We don't use transform for this step of processing so we don't + * need to check whether any nodes have changed transform. + */ + const canSkip = !(forceRecalculation || + (isShared && this.isSharedProjectionDirty) || + this.isProjectionDirty || + this.parent?.isProjectionDirty || + this.attemptToResolveRelativeTarget || + this.root.updateBlockedByResize); + if (canSkip) + return; + const { layout, layoutId } = this.options; + /** + * If we have no layout, we can't perform projection, so early return + */ + if (!this.layout || !(layout || layoutId)) + return; + this.resolvedRelativeTargetAt = motionDom.frameData.timestamp; + /** + * If we don't have a targetDelta but do have a layout, we can attempt to resolve + * a relativeParent. This will allow a component to perform scale correction + * even if no animation has started. + */ + if (!this.targetDelta && !this.relativeTarget) { + const relativeParent = this.getClosestProjectingParent(); + if (relativeParent && + relativeParent.layout && + this.animationProgress !== 1) { + this.relativeParent = relativeParent; + this.forceRelativeParentToResolveTarget(); + this.relativeTarget = createBox(); + this.relativeTargetOrigin = createBox(); + calcRelativePosition(this.relativeTargetOrigin, this.layout.layoutBox, relativeParent.layout.layoutBox); + copyBoxInto(this.relativeTarget, this.relativeTargetOrigin); + } + else { + this.relativeParent = this.relativeTarget = undefined; + } + } + /** + * If we have no relative target or no target delta our target isn't valid + * for this frame. + */ + if (!this.relativeTarget && !this.targetDelta) + return; + /** + * Lazy-init target data structure + */ + if (!this.target) { + this.target = createBox(); + this.targetWithTransforms = createBox(); + } + /** + * If we've got a relative box for this component, resolve it into a target relative to the parent. + */ + if (this.relativeTarget && + this.relativeTargetOrigin && + this.relativeParent && + this.relativeParent.target) { + this.forceRelativeParentToResolveTarget(); + calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target); + /** + * If we've only got a targetDelta, resolve it into a target + */ + } + else if (this.targetDelta) { + if (Boolean(this.resumingFrom)) { + // TODO: This is creating a new object every frame + this.target = this.applyTransform(this.layout.layoutBox); + } + else { + copyBoxInto(this.target, this.layout.layoutBox); + } + applyBoxDelta(this.target, this.targetDelta); + } + else { + /** + * If no target, use own layout as target + */ + copyBoxInto(this.target, this.layout.layoutBox); + } + /** + * If we've been told to attempt to resolve a relative target, do so. + */ + if (this.attemptToResolveRelativeTarget) { + this.attemptToResolveRelativeTarget = false; + const relativeParent = this.getClosestProjectingParent(); + if (relativeParent && + Boolean(relativeParent.resumingFrom) === + Boolean(this.resumingFrom) && + !relativeParent.options.layoutScroll && + relativeParent.target && + this.animationProgress !== 1) { + this.relativeParent = relativeParent; + this.forceRelativeParentToResolveTarget(); + this.relativeTarget = createBox(); + this.relativeTargetOrigin = createBox(); + calcRelativePosition(this.relativeTargetOrigin, this.target, relativeParent.target); + copyBoxInto(this.relativeTarget, this.relativeTargetOrigin); + } + else { + this.relativeParent = this.relativeTarget = undefined; + } + } + /** + * Increase debug counter for resolved target deltas + */ + if (motionDom.statsBuffer.value) { + metrics.calculatedTargetDeltas++; + } + } + getClosestProjectingParent() { + if (!this.parent || + hasScale(this.parent.latestValues) || + has2DTranslate(this.parent.latestValues)) { + return undefined; + } + if (this.parent.isProjecting()) { + return this.parent; + } + else { + return this.parent.getClosestProjectingParent(); + } + } + isProjecting() { + return Boolean((this.relativeTarget || + this.targetDelta || + this.options.layoutRoot) && + this.layout); + } + calcProjection() { + const lead = this.getLead(); + const isShared = Boolean(this.resumingFrom) || this !== lead; + let canSkip = true; + /** + * If this is a normal layout animation and neither this node nor its nearest projecting + * is dirty then we can't skip. + */ + if (this.isProjectionDirty || this.parent?.isProjectionDirty) { + canSkip = false; + } + /** + * If this is a shared layout animation and this node's shared projection is dirty then + * we can't skip. + */ + if (isShared && + (this.isSharedProjectionDirty || this.isTransformDirty)) { + canSkip = false; + } + /** + * If we have resolved the target this frame we must recalculate the + * projection to ensure it visually represents the internal calculations. + */ + if (this.resolvedRelativeTargetAt === motionDom.frameData.timestamp) { + canSkip = false; + } + if (canSkip) + return; + const { layout, layoutId } = this.options; + /** + * If this section of the tree isn't animating we can + * delete our target sources for the following frame. + */ + this.isTreeAnimating = Boolean((this.parent && this.parent.isTreeAnimating) || + this.currentAnimation || + this.pendingAnimation); + if (!this.isTreeAnimating) { + this.targetDelta = this.relativeTarget = undefined; + } + if (!this.layout || !(layout || layoutId)) + return; + /** + * Reset the corrected box with the latest values from box, as we're then going + * to perform mutative operations on it. + */ + copyBoxInto(this.layoutCorrected, this.layout.layoutBox); + /** + * Record previous tree scales before updating. + */ + const prevTreeScaleX = this.treeScale.x; + const prevTreeScaleY = this.treeScale.y; + /** + * Apply all the parent deltas to this box to produce the corrected box. This + * is the layout box, as it will appear on screen as a result of the transforms of its parents. + */ + applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared); + /** + * If this layer needs to perform scale correction but doesn't have a target, + * use the layout as the target. + */ + if (lead.layout && + !lead.target && + (this.treeScale.x !== 1 || this.treeScale.y !== 1)) { + lead.target = lead.layout.layoutBox; + lead.targetWithTransforms = createBox(); + } + const { target } = lead; + if (!target) { + /** + * If we don't have a target to project into, but we were previously + * projecting, we want to remove the stored transform and schedule + * a render to ensure the elements reflect the removed transform. + */ + if (this.prevProjectionDelta) { + this.createProjectionDeltas(); + this.scheduleRender(); + } + return; + } + if (!this.projectionDelta || !this.prevProjectionDelta) { + this.createProjectionDeltas(); + } + else { + copyAxisDeltaInto(this.prevProjectionDelta.x, this.projectionDelta.x); + copyAxisDeltaInto(this.prevProjectionDelta.y, this.projectionDelta.y); + } + /** + * Update the delta between the corrected box and the target box before user-set transforms were applied. + * This will allow us to calculate the corrected borderRadius and boxShadow to compensate + * for our layout reprojection, but still allow them to be scaled correctly by the user. + * It might be that to simplify this we may want to accept that user-set scale is also corrected + * and we wouldn't have to keep and calc both deltas, OR we could support a user setting + * to allow people to choose whether these styles are corrected based on just the + * layout reprojection or the final bounding box. + */ + calcBoxDelta(this.projectionDelta, this.layoutCorrected, target, this.latestValues); + if (this.treeScale.x !== prevTreeScaleX || + this.treeScale.y !== prevTreeScaleY || + !axisDeltaEquals(this.projectionDelta.x, this.prevProjectionDelta.x) || + !axisDeltaEquals(this.projectionDelta.y, this.prevProjectionDelta.y)) { + this.hasProjected = true; + this.scheduleRender(); + this.notifyListeners("projectionUpdate", target); + } + /** + * Increase debug counter for recalculated projections + */ + if (motionDom.statsBuffer.value) { + metrics.calculatedProjections++; + } + } + hide() { + this.isVisible = false; + // TODO: Schedule render + } + show() { + this.isVisible = true; + // TODO: Schedule render + } + scheduleRender(notifyAll = true) { + this.options.visualElement?.scheduleRender(); + if (notifyAll) { + const stack = this.getStack(); + stack && stack.scheduleRender(); + } + if (this.resumingFrom && !this.resumingFrom.instance) { + this.resumingFrom = undefined; + } + } + createProjectionDeltas() { + this.prevProjectionDelta = createDelta(); + this.projectionDelta = createDelta(); + this.projectionDeltaWithTransform = createDelta(); + } + setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) { + const snapshot = this.snapshot; + const snapshotLatestValues = snapshot ? snapshot.latestValues : {}; + const mixedValues = { ...this.latestValues }; + const targetDelta = createDelta(); + if (!this.relativeParent || + !this.relativeParent.options.layoutRoot) { + this.relativeTarget = this.relativeTargetOrigin = undefined; + } + this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged; + const relativeLayout = createBox(); + const snapshotSource = snapshot ? snapshot.source : undefined; + const layoutSource = this.layout ? this.layout.source : undefined; + const isSharedLayoutAnimation = snapshotSource !== layoutSource; + const stack = this.getStack(); + const isOnlyMember = !stack || stack.members.length <= 1; + const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation && + !isOnlyMember && + this.options.crossfade === true && + !this.path.some(hasOpacityCrossfade)); + this.animationProgress = 0; + let prevRelativeTarget; + this.mixTargetDelta = (latest) => { + const progress = latest / 1000; + mixAxisDelta(targetDelta.x, delta.x, progress); + mixAxisDelta(targetDelta.y, delta.y, progress); + this.setTargetDelta(targetDelta); + if (this.relativeTarget && + this.relativeTargetOrigin && + this.layout && + this.relativeParent && + this.relativeParent.layout) { + calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox); + mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress); + /** + * If this is an unchanged relative target we can consider the + * projection not dirty. + */ + if (prevRelativeTarget && + boxEquals(this.relativeTarget, prevRelativeTarget)) { + this.isProjectionDirty = false; + } + if (!prevRelativeTarget) + prevRelativeTarget = createBox(); + copyBoxInto(prevRelativeTarget, this.relativeTarget); + } + if (isSharedLayoutAnimation) { + this.animationValues = mixedValues; + mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember); + } + this.root.scheduleUpdateProjection(); + this.scheduleRender(); + this.animationProgress = progress; + }; + this.mixTargetDelta(this.options.layoutRoot ? 1000 : 0); + } + startAnimation(options) { + this.notifyListeners("animationStart"); + this.currentAnimation?.stop(); + this.resumingFrom?.currentAnimation?.stop(); + if (this.pendingAnimation) { + motionDom.cancelFrame(this.pendingAnimation); + this.pendingAnimation = undefined; + } + /** + * Start the animation in the next frame to have a frame with progress 0, + * where the target is the same as when the animation started, so we can + * calculate the relative positions correctly for instant transitions. + */ + this.pendingAnimation = motionDom.frame.update(() => { + globalProjectionState.hasAnimatedSinceResize = true; + motionDom.activeAnimations.layout++; + this.motionValue || (this.motionValue = motionDom.motionValue(0)); + this.currentAnimation = animateSingleValue(this.motionValue, [0, 1000], { + ...options, + velocity: 0, + isSync: true, + onUpdate: (latest) => { + this.mixTargetDelta(latest); + options.onUpdate && options.onUpdate(latest); + }, + onStop: () => { + motionDom.activeAnimations.layout--; + }, + onComplete: () => { + motionDom.activeAnimations.layout--; + options.onComplete && options.onComplete(); + this.completeAnimation(); + }, + }); + if (this.resumingFrom) { + this.resumingFrom.currentAnimation = this.currentAnimation; + } + this.pendingAnimation = undefined; + }); + } + completeAnimation() { + if (this.resumingFrom) { + this.resumingFrom.currentAnimation = undefined; + this.resumingFrom.preserveOpacity = undefined; + } + const stack = this.getStack(); + stack && stack.exitAnimationComplete(); + this.resumingFrom = + this.currentAnimation = + this.animationValues = + undefined; + this.notifyListeners("animationComplete"); + } + finishAnimation() { + if (this.currentAnimation) { + this.mixTargetDelta && this.mixTargetDelta(animationTarget); + this.currentAnimation.stop(); + } + this.completeAnimation(); + } + applyTransformsToTarget() { + const lead = this.getLead(); + let { targetWithTransforms, target, layout, latestValues } = lead; + if (!targetWithTransforms || !target || !layout) + return; + /** + * If we're only animating position, and this element isn't the lead element, + * then instead of projecting into the lead box we instead want to calculate + * a new target that aligns the two boxes but maintains the layout shape. + */ + if (this !== lead && + this.layout && + layout && + shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) { + target = this.target || createBox(); + const xLength = calcLength(this.layout.layoutBox.x); + target.x.min = lead.target.x.min; + target.x.max = target.x.min + xLength; + const yLength = calcLength(this.layout.layoutBox.y); + target.y.min = lead.target.y.min; + target.y.max = target.y.min + yLength; + } + copyBoxInto(targetWithTransforms, target); + /** + * Apply the latest user-set transforms to the targetBox to produce the targetBoxFinal. + * This is the final box that we will then project into by calculating a transform delta and + * applying it to the corrected box. + */ + transformBox(targetWithTransforms, latestValues); + /** + * Update the delta between the corrected box and the final target box, after + * user-set transforms are applied to it. This will be used by the renderer to + * create a transform style that will reproject the element from its layout layout + * into the desired bounding box. + */ + calcBoxDelta(this.projectionDeltaWithTransform, this.layoutCorrected, targetWithTransforms, latestValues); + } + registerSharedNode(layoutId, node) { + if (!this.sharedNodes.has(layoutId)) { + this.sharedNodes.set(layoutId, new NodeStack()); + } + const stack = this.sharedNodes.get(layoutId); + stack.add(node); + const config = node.options.initialPromotionConfig; + node.promote({ + transition: config ? config.transition : undefined, + preserveFollowOpacity: config && config.shouldPreserveFollowOpacity + ? config.shouldPreserveFollowOpacity(node) + : undefined, + }); + } + isLead() { + const stack = this.getStack(); + return stack ? stack.lead === this : true; + } + getLead() { + const { layoutId } = this.options; + return layoutId ? this.getStack()?.lead || this : this; + } + getPrevLead() { + const { layoutId } = this.options; + return layoutId ? this.getStack()?.prevLead : undefined; + } + getStack() { + const { layoutId } = this.options; + if (layoutId) + return this.root.sharedNodes.get(layoutId); + } + promote({ needsReset, transition, preserveFollowOpacity, } = {}) { + const stack = this.getStack(); + if (stack) + stack.promote(this, preserveFollowOpacity); + if (needsReset) { + this.projectionDelta = undefined; + this.needsReset = true; + } + if (transition) + this.setOptions({ transition }); + } + relegate() { + const stack = this.getStack(); + if (stack) { + return stack.relegate(this); + } + else { + return false; + } + } + resetSkewAndRotation() { + const { visualElement } = this.options; + if (!visualElement) + return; + // If there's no detected skew or rotation values, we can early return without a forced render. + let hasDistortingTransform = false; + /** + * An unrolled check for rotation values. Most elements don't have any rotation and + * skipping the nested loop and new object creation is 50% faster. + */ + const { latestValues } = visualElement; + if (latestValues.z || + latestValues.rotate || + latestValues.rotateX || + latestValues.rotateY || + latestValues.rotateZ || + latestValues.skewX || + latestValues.skewY) { + hasDistortingTransform = true; + } + // If there's no distorting values, we don't need to do any more. + if (!hasDistortingTransform) + return; + const resetValues = {}; + if (latestValues.z) { + resetDistortingTransform("z", visualElement, resetValues, this.animationValues); + } + // Check the skew and rotate value of all axes and reset to 0 + for (let i = 0; i < transformAxes.length; i++) { + resetDistortingTransform(`rotate${transformAxes[i]}`, visualElement, resetValues, this.animationValues); + resetDistortingTransform(`skew${transformAxes[i]}`, visualElement, resetValues, this.animationValues); + } + // Force a render of this element to apply the transform with all skews and rotations + // set to 0. + visualElement.render(); + // Put back all the values we reset + for (const key in resetValues) { + visualElement.setStaticValue(key, resetValues[key]); + if (this.animationValues) { + this.animationValues[key] = resetValues[key]; + } + } + // Schedule a render for the next frame. This ensures we won't visually + // see the element with the reset rotate value applied. + visualElement.scheduleRender(); + } + applyProjectionStyles(targetStyle, // CSSStyleDeclaration - doesn't allow numbers to be assigned to properties + styleProp) { + if (!this.instance || this.isSVG) + return; + if (!this.isVisible) { + targetStyle.visibility = "hidden"; + return; + } + const transformTemplate = this.getTransformTemplate(); + if (this.needsReset) { + this.needsReset = false; + targetStyle.visibility = ""; + targetStyle.opacity = ""; + targetStyle.pointerEvents = + resolveMotionValue(styleProp?.pointerEvents) || ""; + targetStyle.transform = transformTemplate + ? transformTemplate(this.latestValues, "") + : "none"; + return; + } + const lead = this.getLead(); + if (!this.projectionDelta || !this.layout || !lead.target) { + if (this.options.layoutId) { + targetStyle.opacity = + this.latestValues.opacity !== undefined + ? this.latestValues.opacity + : 1; + targetStyle.pointerEvents = + resolveMotionValue(styleProp?.pointerEvents) || ""; + } + if (this.hasProjected && !hasTransform(this.latestValues)) { + targetStyle.transform = transformTemplate + ? transformTemplate({}, "") + : "none"; + this.hasProjected = false; + } + return; + } + targetStyle.visibility = ""; + const valuesToRender = lead.animationValues || lead.latestValues; + this.applyTransformsToTarget(); + let transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender); + if (transformTemplate) { + transform = transformTemplate(valuesToRender, transform); + } + targetStyle.transform = transform; + const { x, y } = this.projectionDelta; + targetStyle.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`; + if (lead.animationValues) { + /** + * If the lead component is animating, assign this either the entering/leaving + * opacity + */ + targetStyle.opacity = + lead === this + ? valuesToRender.opacity ?? + this.latestValues.opacity ?? + 1 + : this.preserveOpacity + ? this.latestValues.opacity + : valuesToRender.opacityExit; + } + else { + /** + * Or we're not animating at all, set the lead component to its layout + * opacity and other components to hidden. + */ + targetStyle.opacity = + lead === this + ? valuesToRender.opacity !== undefined + ? valuesToRender.opacity + : "" + : valuesToRender.opacityExit !== undefined + ? valuesToRender.opacityExit + : 0; + } + /** + * Apply scale correction + */ + for (const key in scaleCorrectors) { + if (valuesToRender[key] === undefined) + continue; + const { correct, applyTo, isCSSVariable } = scaleCorrectors[key]; + /** + * Only apply scale correction to the value if we have an + * active projection transform. Otherwise these values become + * vulnerable to distortion if the element changes size without + * a corresponding layout animation. + */ + const corrected = transform === "none" + ? valuesToRender[key] + : correct(valuesToRender[key], lead); + if (applyTo) { + const num = applyTo.length; + for (let i = 0; i < num; i++) { + targetStyle[applyTo[i]] = corrected; + } + } + else { + // If this is a CSS variable, set it directly on the instance. + // Replacing this function from creating styles to setting them + // would be a good place to remove per frame object creation + if (isCSSVariable) { + this.options.visualElement.renderState.vars[key] = corrected; + } + else { + targetStyle[key] = corrected; + } + } + } + /** + * Disable pointer events on follow components. This is to ensure + * that if a follow component covers a lead component it doesn't block + * pointer events on the lead. + */ + if (this.options.layoutId) { + targetStyle.pointerEvents = + lead === this + ? resolveMotionValue(styleProp?.pointerEvents) || "" + : "none"; + } + } + clearSnapshot() { + this.resumeFrom = this.snapshot = undefined; + } + // Only run on root + resetTree() { + this.root.nodes.forEach((node) => node.currentAnimation?.stop()); + this.root.nodes.forEach(clearMeasurements); + this.root.sharedNodes.clear(); + } + }; +} +function updateLayout(node) { + node.updateLayout(); +} +function notifyLayoutUpdate(node) { + const snapshot = node.resumeFrom?.snapshot || node.snapshot; + if (node.isLead() && + node.layout && + snapshot && + node.hasListeners("didUpdate")) { + const { layoutBox: layout, measuredBox: measuredLayout } = node.layout; + const { animationType } = node.options; + const isShared = snapshot.source !== node.layout.source; + // TODO Maybe we want to also resize the layout snapshot so we don't trigger + // animations for instance if layout="size" and an element has only changed position + if (animationType === "size") { + eachAxis((axis) => { + const axisSnapshot = isShared + ? snapshot.measuredBox[axis] + : snapshot.layoutBox[axis]; + const length = calcLength(axisSnapshot); + axisSnapshot.min = layout[axis].min; + axisSnapshot.max = axisSnapshot.min + length; + }); + } + else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) { + eachAxis((axis) => { + const axisSnapshot = isShared + ? snapshot.measuredBox[axis] + : snapshot.layoutBox[axis]; + const length = calcLength(layout[axis]); + axisSnapshot.max = axisSnapshot.min + length; + /** + * Ensure relative target gets resized and rerendererd + */ + if (node.relativeTarget && !node.currentAnimation) { + node.isProjectionDirty = true; + node.relativeTarget[axis].max = + node.relativeTarget[axis].min + length; + } + }); + } + const layoutDelta = createDelta(); + calcBoxDelta(layoutDelta, layout, snapshot.layoutBox); + const visualDelta = createDelta(); + if (isShared) { + calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox); + } + else { + calcBoxDelta(visualDelta, layout, snapshot.layoutBox); + } + const hasLayoutChanged = !isDeltaZero(layoutDelta); + let hasRelativeLayoutChanged = false; + if (!node.resumeFrom) { + const relativeParent = node.getClosestProjectingParent(); + /** + * If the relativeParent is itself resuming from a different element then + * the relative snapshot is not relavent + */ + if (relativeParent && !relativeParent.resumeFrom) { + const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent; + if (parentSnapshot && parentLayout) { + const relativeSnapshot = createBox(); + calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox); + const relativeLayout = createBox(); + calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox); + if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) { + hasRelativeLayoutChanged = true; + } + if (relativeParent.options.layoutRoot) { + node.relativeTarget = relativeLayout; + node.relativeTargetOrigin = relativeSnapshot; + node.relativeParent = relativeParent; + } + } + } + } + node.notifyListeners("didUpdate", { + layout, + snapshot, + delta: visualDelta, + layoutDelta, + hasLayoutChanged, + hasRelativeLayoutChanged, + }); + } + else if (node.isLead()) { + const { onExitComplete } = node.options; + onExitComplete && onExitComplete(); + } + /** + * Clearing transition + * TODO: Investigate why this transition is being passed in as {type: false } from Framer + * and why we need it at all + */ + node.options.transition = undefined; +} +function propagateDirtyNodes(node) { + /** + * Increase debug counter for nodes encountered this frame + */ + if (motionDom.statsBuffer.value) { + metrics.nodes++; + } + if (!node.parent) + return; + /** + * If this node isn't projecting, propagate isProjectionDirty. It will have + * no performance impact but it will allow the next child that *is* projecting + * but *isn't* dirty to just check its parent to see if *any* ancestor needs + * correcting. + */ + if (!node.isProjecting()) { + node.isProjectionDirty = node.parent.isProjectionDirty; + } + /** + * Propagate isSharedProjectionDirty and isTransformDirty + * throughout the whole tree. A future revision can take another look at + * this but for safety we still recalcualte shared nodes. + */ + node.isSharedProjectionDirty || (node.isSharedProjectionDirty = Boolean(node.isProjectionDirty || + node.parent.isProjectionDirty || + node.parent.isSharedProjectionDirty)); + node.isTransformDirty || (node.isTransformDirty = node.parent.isTransformDirty); +} +function cleanDirtyNodes(node) { + node.isProjectionDirty = + node.isSharedProjectionDirty = + node.isTransformDirty = + false; +} +function clearSnapshot(node) { + node.clearSnapshot(); +} +function clearMeasurements(node) { + node.clearMeasurements(); +} +function clearIsLayoutDirty(node) { + node.isLayoutDirty = false; +} +function resetTransformStyle(node) { + const { visualElement } = node.options; + if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) { + visualElement.notify("BeforeLayoutMeasure"); + } + node.resetTransform(); +} +function finishAnimation(node) { + node.finishAnimation(); + node.targetDelta = node.relativeTarget = node.target = undefined; + node.isProjectionDirty = true; +} +function resolveTargetDelta(node) { + node.resolveTargetDelta(); +} +function calcProjection(node) { + node.calcProjection(); +} +function resetSkewAndRotation(node) { + node.resetSkewAndRotation(); +} +function removeLeadSnapshots(stack) { + stack.removeLeadSnapshot(); +} +function mixAxisDelta(output, delta, p) { + output.translate = motionDom.mixNumber(delta.translate, 0, p); + output.scale = motionDom.mixNumber(delta.scale, 1, p); + output.origin = delta.origin; + output.originPoint = delta.originPoint; +} +function mixAxis(output, from, to, p) { + output.min = motionDom.mixNumber(from.min, to.min, p); + output.max = motionDom.mixNumber(from.max, to.max, p); +} +function mixBox(output, from, to, p) { + mixAxis(output.x, from.x, to.x, p); + mixAxis(output.y, from.y, to.y, p); +} +function hasOpacityCrossfade(node) { + return (node.animationValues && node.animationValues.opacityExit !== undefined); +} +const defaultLayoutTransition = { + duration: 0.45, + ease: [0.4, 0, 0.1, 1], +}; +const userAgentContains = (string) => typeof navigator !== "undefined" && + navigator.userAgent && + navigator.userAgent.toLowerCase().includes(string); +/** + * Measured bounding boxes must be rounded in Safari and + * left untouched in Chrome, otherwise non-integer layouts within scaled-up elements + * can appear to jump. + */ +const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/") + ? Math.round + : motionUtils.noop; +function roundAxis(axis) { + // Round to the nearest .5 pixels to support subpixel layouts + axis.min = roundPoint(axis.min); + axis.max = roundPoint(axis.max); +} +function roundBox(box) { + roundAxis(box.x); + roundAxis(box.y); +} +function shouldAnimatePositionOnly(animationType, snapshot, layout) { + return (animationType === "position" || + (animationType === "preserve-aspect" && + !isNear(aspectRatio(snapshot), aspectRatio(layout), 0.2))); +} +function checkNodeWasScrollRoot(node) { + return node !== node.root && node.scroll?.wasRoot; +} + +function addDomEvent(target, eventName, handler, options = { passive: true }) { + target.addEventListener(eventName, handler, options); + return () => target.removeEventListener(eventName, handler); +} + +const DocumentProjectionNode = createProjectionNode$1({ + attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify), + measureScroll: () => ({ + x: document.documentElement.scrollLeft || document.body.scrollLeft, + y: document.documentElement.scrollTop || document.body.scrollTop, + }), + checkIsScrollRoot: () => true, +}); + +const rootProjectionNode = { + current: undefined, +}; +const HTMLProjectionNode = createProjectionNode$1({ + measureScroll: (instance) => ({ + x: instance.scrollLeft, + y: instance.scrollTop, + }), + defaultParent: () => { + if (!rootProjectionNode.current) { + const documentNode = new DocumentProjectionNode({}); + documentNode.mount(window); + documentNode.setOptions({ layoutScroll: true }); + rootProjectionNode.current = documentNode; + } + return rootProjectionNode.current; + }, + resetTransform: (instance, value) => { + instance.style.transform = value !== undefined ? value : "none"; + }, + checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"), +}); + +function pixelsToPercent(pixels, axis) { + if (axis.max === axis.min) + return 0; + return (pixels / (axis.max - axis.min)) * 100; +} +/** + * We always correct borderRadius as a percentage rather than pixels to reduce paints. + * For example, if you are projecting a box that is 100px wide with a 10px borderRadius + * into a box that is 200px wide with a 20px borderRadius, that is actually a 10% + * borderRadius in both states. If we animate between the two in pixels that will trigger + * a paint each time. If we animate between the two in percentage we'll avoid a paint. + */ +const correctBorderRadius = { + correct: (latest, node) => { + if (!node.target) + return latest; + /** + * If latest is a string, if it's a percentage we can return immediately as it's + * going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number. + */ + if (typeof latest === "string") { + if (motionDom.px.test(latest)) { + latest = parseFloat(latest); + } + else { + return latest; + } + } + /** + * If latest is a number, it's a pixel value. We use the current viewportBox to calculate that + * pixel value as a percentage of each axis + */ + const x = pixelsToPercent(latest, node.target.x); + const y = pixelsToPercent(latest, node.target.y); + return `${x}% ${y}%`; + }, +}; + +const correctBoxShadow = { + correct: (latest, { treeScale, projectionDelta }) => { + const original = latest; + const shadow = motionDom.complex.parse(latest); + // TODO: Doesn't support multiple shadows + if (shadow.length > 5) + return original; + const template = motionDom.complex.createTransformer(latest); + const offset = typeof shadow[0] !== "number" ? 1 : 0; + // Calculate the overall context scale + const xScale = projectionDelta.x.scale * treeScale.x; + const yScale = projectionDelta.y.scale * treeScale.y; + shadow[0 + offset] /= xScale; + shadow[1 + offset] /= yScale; + /** + * Ideally we'd correct x and y scales individually, but because blur and + * spread apply to both we have to take a scale average and apply that instead. + * We could potentially improve the outcome of this by incorporating the ratio between + * the two scales. + */ + const averageScale = motionDom.mixNumber(xScale, yScale, 0.5); + // Blur + if (typeof shadow[2 + offset] === "number") + shadow[2 + offset] /= averageScale; + // Spread + if (typeof shadow[3 + offset] === "number") + shadow[3 + offset] /= averageScale; + return template(shadow); + }, +}; + +/** + * Bounding boxes tend to be defined as top, left, right, bottom. For various operations + * it's easier to consider each axis individually. This function returns a bounding box + * as a map of single-axis min/max values. + */ +function convertBoundingBoxToBox({ top, left, right, bottom, }) { + return { + x: { min: left, max: right }, + y: { min: top, max: bottom }, + }; +} +function convertBoxToBoundingBox({ x, y }) { + return { top: y.min, right: x.max, bottom: y.max, left: x.min }; +} +/** + * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function + * provided by Framer to allow measured points to be corrected for device scaling. This is used + * when measuring DOM elements and DOM event points. + */ +function transformBoxPoints(point, transformPoint) { + if (!transformPoint) + return point; + const topLeft = transformPoint({ x: point.left, y: point.top }); + const bottomRight = transformPoint({ x: point.right, y: point.bottom }); + return { + top: topLeft.y, + left: topLeft.x, + bottom: bottomRight.y, + right: bottomRight.x, + }; +} + +function measureViewportBox(instance, transformPoint) { + return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint)); +} +function measurePageBox(element, rootProjectionNode, transformPagePoint) { + const viewportBox = measureViewportBox(element, transformPagePoint); + const { scroll } = rootProjectionNode; + if (scroll) { + translateAxis(viewportBox.x, scroll.offset.x); + translateAxis(viewportBox.y, scroll.offset.y); + } + return viewportBox; +} + +const featureProps = { + animation: [ + "animate", + "variants", + "whileHover", + "whileTap", + "exit", + "whileInView", + "whileFocus", + "whileDrag", + ], + exit: ["exit"], + drag: ["drag", "dragControls"], + focus: ["whileFocus"], + hover: ["whileHover", "onHoverStart", "onHoverEnd"], + tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"], + pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"], + inView: ["whileInView", "onViewportEnter", "onViewportLeave"], + layout: ["layout", "layoutId"], +}; +const featureDefinitions = {}; +for (const key in featureProps) { + featureDefinitions[key] = { + isEnabled: (props) => featureProps[key].some((name) => !!props[name]), + }; +} + +// Does this device prefer reduced motion? Returns `null` server-side. +const prefersReducedMotion = { current: null }; +const hasReducedMotionListener = { current: false }; + +function initPrefersReducedMotion() { + hasReducedMotionListener.current = true; + if (!isBrowser) + return; + if (window.matchMedia) { + const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)"); + const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches); + motionMediaQuery.addEventListener("change", setReducedMotionPreferences); + setReducedMotionPreferences(); + } + else { + prefersReducedMotion.current = false; + } +} + +const visualElementStore = new WeakMap(); + +function isAnimationControls(v) { + return (v !== null && + typeof v === "object" && + typeof v.start === "function"); +} + +/** + * Decides if the supplied variable is variant label + */ +function isVariantLabel(v) { + return typeof v === "string" || Array.isArray(v); +} + +const variantPriorityOrder = [ + "animate", + "whileInView", + "whileFocus", + "whileHover", + "whileTap", + "whileDrag", + "exit", +]; +const variantProps = ["initial", ...variantPriorityOrder]; + +function isControllingVariants(props) { + return (isAnimationControls(props.animate) || + variantProps.some((name) => isVariantLabel(props[name]))); +} +function isVariantNode(props) { + return Boolean(isControllingVariants(props) || props.variants); +} + +function updateMotionValuesFromProps(element, next, prev) { + for (const key in next) { + const nextValue = next[key]; + const prevValue = prev[key]; + if (motionDom.isMotionValue(nextValue)) { + /** + * If this is a motion value found in props or style, we want to add it + * to our visual element's motion value map. + */ + element.addValue(key, nextValue); + } + else if (motionDom.isMotionValue(prevValue)) { + /** + * If we're swapping from a motion value to a static value, + * create a new motion value from that + */ + element.addValue(key, motionDom.motionValue(nextValue, { owner: element })); + } + else if (prevValue !== nextValue) { + /** + * If this is a flat value that has changed, update the motion value + * or create one if it doesn't exist. We only want to do this if we're + * not handling the value with our animation state. + */ + if (element.hasValue(key)) { + const existingValue = element.getValue(key); + if (existingValue.liveStyle === true) { + existingValue.jump(nextValue); + } + else if (!existingValue.hasAnimated) { + existingValue.set(nextValue); + } + } + else { + const latestValue = element.getStaticValue(key); + element.addValue(key, motionDom.motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element })); + } + } + } + // Handle removed values + for (const key in prev) { + if (next[key] === undefined) + element.removeValue(key); + } + return next; +} + +function getValueState(visualElement) { + const state = [{}, {}]; + visualElement?.values.forEach((value, key) => { + state[0][key] = value.get(); + state[1][key] = value.getVelocity(); + }); + return state; +} +function resolveVariantFromProps(props, definition, custom, visualElement) { + /** + * If the variant definition is a function, resolve. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + /** + * If the variant definition is a variant label, or + * the function returned a variant label, resolve. + */ + if (typeof definition === "string") { + definition = props.variants && props.variants[definition]; + } + /** + * At this point we've resolved both functions and variant labels, + * but the resolved variant label might itself have been a function. + * If so, resolve. This can only have returned a valid target object. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + return definition; +} + +const propEventHandlers = [ + "AnimationStart", + "AnimationComplete", + "Update", + "BeforeLayoutMeasure", + "LayoutMeasure", + "LayoutAnimationStart", + "LayoutAnimationComplete", +]; +/** + * A VisualElement is an imperative abstraction around UI elements such as + * HTMLElement, SVGElement, Three.Object3D etc. + */ +class VisualElement { + /** + * This method takes React props and returns found MotionValues. For example, HTML + * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays. + * + * This isn't an abstract method as it needs calling in the constructor, but it is + * intended to be one. + */ + scrapeMotionValuesFromProps(_props, _prevProps, _visualElement) { + return {}; + } + constructor({ parent, props, presenceContext, reducedMotionConfig, blockInitialAnimation, visualState, }, options = {}) { + /** + * A reference to the current underlying Instance, e.g. a HTMLElement + * or Three.Mesh etc. + */ + this.current = null; + /** + * A set containing references to this VisualElement's children. + */ + this.children = new Set(); + /** + * Determine what role this visual element should take in the variant tree. + */ + this.isVariantNode = false; + this.isControllingVariants = false; + /** + * Decides whether this VisualElement should animate in reduced motion + * mode. + * + * TODO: This is currently set on every individual VisualElement but feels + * like it could be set globally. + */ + this.shouldReduceMotion = null; + /** + * A map of all motion values attached to this visual element. Motion + * values are source of truth for any given animated value. A motion + * value might be provided externally by the component via props. + */ + this.values = new Map(); + this.KeyframeResolver = motionDom.KeyframeResolver; + /** + * Cleanup functions for active features (hover/tap/exit etc) + */ + this.features = {}; + /** + * A map of every subscription that binds the provided or generated + * motion values onChange listeners to this visual element. + */ + this.valueSubscriptions = new Map(); + /** + * A reference to the previously-provided motion values as returned + * from scrapeMotionValuesFromProps. We use the keys in here to determine + * if any motion values need to be removed after props are updated. + */ + this.prevMotionValues = {}; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + /** + * An object containing an unsubscribe function for each prop event subscription. + * For example, every "Update" event can have multiple subscribers via + * VisualElement.on(), but only one of those can be defined via the onUpdate prop. + */ + this.propEventSubscriptions = {}; + this.notifyUpdate = () => this.notify("Update", this.latestValues); + this.render = () => { + if (!this.current) + return; + this.triggerBuild(); + this.renderInstance(this.current, this.renderState, this.props.style, this.projection); + }; + this.renderScheduledAt = 0.0; + this.scheduleRender = () => { + const now = motionDom.time.now(); + if (this.renderScheduledAt < now) { + this.renderScheduledAt = now; + motionDom.frame.render(this.render, false, true); + } + }; + const { latestValues, renderState } = visualState; + this.latestValues = latestValues; + this.baseTarget = { ...latestValues }; + this.initialValues = props.initial ? { ...latestValues } : {}; + this.renderState = renderState; + this.parent = parent; + this.props = props; + this.presenceContext = presenceContext; + this.depth = parent ? parent.depth + 1 : 0; + this.reducedMotionConfig = reducedMotionConfig; + this.options = options; + this.blockInitialAnimation = Boolean(blockInitialAnimation); + this.isControllingVariants = isControllingVariants(props); + this.isVariantNode = isVariantNode(props); + if (this.isVariantNode) { + this.variantChildren = new Set(); + } + this.manuallyAnimateOnMount = Boolean(parent && parent.current); + /** + * Any motion values that are provided to the element when created + * aren't yet bound to the element, as this would technically be impure. + * However, we iterate through the motion values and set them to the + * initial values for this component. + * + * TODO: This is impure and we should look at changing this to run on mount. + * Doing so will break some tests but this isn't necessarily a breaking change, + * more a reflection of the test. + */ + const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {}, this); + for (const key in initialMotionValues) { + const value = initialMotionValues[key]; + if (latestValues[key] !== undefined && motionDom.isMotionValue(value)) { + value.set(latestValues[key], false); + } + } + } + mount(instance) { + this.current = instance; + visualElementStore.set(instance, this); + if (this.projection && !this.projection.instance) { + this.projection.mount(instance); + } + if (this.parent && this.isVariantNode && !this.isControllingVariants) { + this.removeFromVariantTree = this.parent.addVariantChild(this); + } + this.values.forEach((value, key) => this.bindToMotionValue(key, value)); + if (!hasReducedMotionListener.current) { + initPrefersReducedMotion(); + } + this.shouldReduceMotion = + this.reducedMotionConfig === "never" + ? false + : this.reducedMotionConfig === "always" + ? true + : prefersReducedMotion.current; + if (process.env.NODE_ENV !== "production") { + motionUtils.warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + if (this.parent) + this.parent.children.add(this); + this.update(this.props, this.presenceContext); + } + unmount() { + this.projection && this.projection.unmount(); + motionDom.cancelFrame(this.notifyUpdate); + motionDom.cancelFrame(this.render); + this.valueSubscriptions.forEach((remove) => remove()); + this.valueSubscriptions.clear(); + this.removeFromVariantTree && this.removeFromVariantTree(); + this.parent && this.parent.children.delete(this); + for (const key in this.events) { + this.events[key].clear(); + } + for (const key in this.features) { + const feature = this.features[key]; + if (feature) { + feature.unmount(); + feature.isMounted = false; + } + } + this.current = null; + } + bindToMotionValue(key, value) { + if (this.valueSubscriptions.has(key)) { + this.valueSubscriptions.get(key)(); + } + const valueIsTransform = motionDom.transformProps.has(key); + if (valueIsTransform && this.onBindTransform) { + this.onBindTransform(); + } + const removeOnChange = value.on("change", (latestValue) => { + this.latestValues[key] = latestValue; + this.props.onUpdate && motionDom.frame.preRender(this.notifyUpdate); + if (valueIsTransform && this.projection) { + this.projection.isTransformDirty = true; + } + }); + const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender); + let removeSyncCheck; + if (window.MotionCheckAppearSync) { + removeSyncCheck = window.MotionCheckAppearSync(this, key, value); + } + this.valueSubscriptions.set(key, () => { + removeOnChange(); + removeOnRenderRequest(); + if (removeSyncCheck) + removeSyncCheck(); + if (value.owner) + value.stop(); + }); + } + sortNodePosition(other) { + /** + * If these nodes aren't even of the same type we can't compare their depth. + */ + if (!this.current || + !this.sortInstanceNodePosition || + this.type !== other.type) { + return 0; + } + return this.sortInstanceNodePosition(this.current, other.current); + } + updateFeatures() { + let key = "animation"; + for (key in featureDefinitions) { + const featureDefinition = featureDefinitions[key]; + if (!featureDefinition) + continue; + const { isEnabled, Feature: FeatureConstructor } = featureDefinition; + /** + * If this feature is enabled but not active, make a new instance. + */ + if (!this.features[key] && + FeatureConstructor && + isEnabled(this.props)) { + this.features[key] = new FeatureConstructor(this); + } + /** + * If we have a feature, mount or update it. + */ + if (this.features[key]) { + const feature = this.features[key]; + if (feature.isMounted) { + feature.update(); + } + else { + feature.mount(); + feature.isMounted = true; + } + } + } + } + triggerBuild() { + this.build(this.renderState, this.latestValues, this.props); + } + /** + * Measure the current viewport box with or without transforms. + * Only measures axis-aligned boxes, rotate and skew must be manually + * removed with a re-render to work. + */ + measureViewportBox() { + return this.current + ? this.measureInstanceViewportBox(this.current, this.props) + : createBox(); + } + getStaticValue(key) { + return this.latestValues[key]; + } + setStaticValue(key, value) { + this.latestValues[key] = value; + } + /** + * Update the provided props. Ensure any newly-added motion values are + * added to our map, old ones removed, and listeners updated. + */ + update(props, presenceContext) { + if (props.transformTemplate || this.props.transformTemplate) { + this.scheduleRender(); + } + this.prevProps = this.props; + this.props = props; + this.prevPresenceContext = this.presenceContext; + this.presenceContext = presenceContext; + /** + * Update prop event handlers ie onAnimationStart, onAnimationComplete + */ + for (let i = 0; i < propEventHandlers.length; i++) { + const key = propEventHandlers[i]; + if (this.propEventSubscriptions[key]) { + this.propEventSubscriptions[key](); + delete this.propEventSubscriptions[key]; + } + const listenerName = ("on" + key); + const listener = props[listenerName]; + if (listener) { + this.propEventSubscriptions[key] = this.on(key, listener); + } + } + this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps, this), this.prevMotionValues); + if (this.handleChildMotionValue) { + this.handleChildMotionValue(); + } + } + getProps() { + return this.props; + } + /** + * Returns the variant definition with a given name. + */ + getVariant(name) { + return this.props.variants ? this.props.variants[name] : undefined; + } + /** + * Returns the defined default transition on this component. + */ + getDefaultTransition() { + return this.props.transition; + } + getTransformPagePoint() { + return this.props.transformPagePoint; + } + getClosestVariantNode() { + return this.isVariantNode + ? this + : this.parent + ? this.parent.getClosestVariantNode() + : undefined; + } + /** + * Add a child visual element to our set of children. + */ + addVariantChild(child) { + const closestVariantNode = this.getClosestVariantNode(); + if (closestVariantNode) { + closestVariantNode.variantChildren && + closestVariantNode.variantChildren.add(child); + return () => closestVariantNode.variantChildren.delete(child); + } + } + /** + * Add a motion value and bind it to this visual element. + */ + addValue(key, value) { + // Remove existing value if it exists + const existingValue = this.values.get(key); + if (value !== existingValue) { + if (existingValue) + this.removeValue(key); + this.bindToMotionValue(key, value); + this.values.set(key, value); + this.latestValues[key] = value.get(); + } + } + /** + * Remove a motion value and unbind any active subscriptions. + */ + removeValue(key) { + this.values.delete(key); + const unsubscribe = this.valueSubscriptions.get(key); + if (unsubscribe) { + unsubscribe(); + this.valueSubscriptions.delete(key); + } + delete this.latestValues[key]; + this.removeValueFromRenderState(key, this.renderState); + } + /** + * Check whether we have a motion value for this key + */ + hasValue(key) { + return this.values.has(key); + } + getValue(key, defaultValue) { + if (this.props.values && this.props.values[key]) { + return this.props.values[key]; + } + let value = this.values.get(key); + if (value === undefined && defaultValue !== undefined) { + value = motionDom.motionValue(defaultValue === null ? undefined : defaultValue, { owner: this }); + this.addValue(key, value); + } + return value; + } + /** + * If we're trying to animate to a previously unencountered value, + * we need to check for it in our state and as a last resort read it + * directly from the instance (which might have performance implications). + */ + readValue(key, target) { + let value = this.latestValues[key] !== undefined || !this.current + ? this.latestValues[key] + : this.getBaseTargetFromProps(this.props, key) ?? + this.readValueFromInstance(this.current, key, this.options); + if (value !== undefined && value !== null) { + if (typeof value === "string" && + (motionUtils.isNumericalString(value) || motionUtils.isZeroValueString(value))) { + // If this is a number read as a string, ie "0" or "200", convert it to a number + value = parseFloat(value); + } + else if (!motionDom.findValueType(value) && motionDom.complex.test(target)) { + value = motionDom.getAnimatableNone(key, target); + } + this.setBaseTarget(key, motionDom.isMotionValue(value) ? value.get() : value); + } + return motionDom.isMotionValue(value) ? value.get() : value; + } + /** + * Set the base target to later animate back to. This is currently + * only hydrated on creation and when we first read a value. + */ + setBaseTarget(key, value) { + this.baseTarget[key] = value; + } + /** + * Find the base target for a value thats been removed from all animation + * props. + */ + getBaseTarget(key) { + const { initial } = this.props; + let valueFromInitial; + if (typeof initial === "string" || typeof initial === "object") { + const variant = resolveVariantFromProps(this.props, initial, this.presenceContext?.custom); + if (variant) { + valueFromInitial = variant[key]; + } + } + /** + * If this value still exists in the current initial variant, read that. + */ + if (initial && valueFromInitial !== undefined) { + return valueFromInitial; + } + /** + * Alternatively, if this VisualElement config has defined a getBaseTarget + * so we can read the value from an alternative source, try that. + */ + const target = this.getBaseTargetFromProps(this.props, key); + if (target !== undefined && !motionDom.isMotionValue(target)) + return target; + /** + * If the value was initially defined on initial, but it doesn't any more, + * return undefined. Otherwise return the value as initially read from the DOM. + */ + return this.initialValues[key] !== undefined && + valueFromInitial === undefined + ? undefined + : this.baseTarget[key]; + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new motionUtils.SubscriptionManager(); + } + return this.events[eventName].add(callback); + } + notify(eventName, ...args) { + if (this.events[eventName]) { + this.events[eventName].notify(...args); + } + } +} + +class DOMVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.KeyframeResolver = motionDom.DOMKeyframesResolver; + } + sortInstanceNodePosition(a, b) { + /** + * compareDocumentPosition returns a bitmask, by using the bitwise & + * we're returning true if 2 in that bitmask is set to true. 2 is set + * to true if b preceeds a. + */ + return a.compareDocumentPosition(b) & 2 ? 1 : -1; + } + getBaseTargetFromProps(props, key) { + return props.style + ? props.style[key] + : undefined; + } + removeValueFromRenderState(key, { vars, style }) { + delete vars[key]; + delete style[key]; + } + handleChildMotionValue() { + if (this.childSubscription) { + this.childSubscription(); + delete this.childSubscription; + } + const { children } = this.props; + if (motionDom.isMotionValue(children)) { + this.childSubscription = children.on("change", (latest) => { + if (this.current) { + this.current.textContent = `${latest}`; + } + }); + } + } +} + +const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", +}; +const numTransforms = motionDom.transformPropOrder.length; +/** + * Build a CSS transform style from individual x/y/scale etc properties. + * + * This outputs with a default order of transforms/scales/rotations, this can be customised by + * providing a transformTemplate function. + */ +function buildTransform(latestValues, transform, transformTemplate) { + // The transform string we're going to build into. + let transformString = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < numTransforms; i++) { + const key = motionDom.transformPropOrder[i]; + const value = latestValues[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault || transformTemplate) { + const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]); + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + transformString += `${transformName}(${valueAsType}) `; + } + if (transformTemplate) { + transform[key] = valueAsType; + } + } + } + transformString = transformString.trim(); + // If we have a custom `transform` template, pass our transform values and + // generated transformString to that before returning + if (transformTemplate) { + transformString = transformTemplate(transform, transformIsDefault ? "" : transformString); + } + else if (transformIsDefault) { + transformString = "none"; + } + return transformString; +} + +function buildHTMLStyles(state, latestValues, transformTemplate) { + const { style, vars, transformOrigin } = state; + // Track whether we encounter any transform or transformOrigin values. + let hasTransform = false; + let hasTransformOrigin = false; + /** + * Loop over all our latest animated values and decide whether to handle them + * as a style or CSS variable. + * + * Transforms and transform origins are kept separately for further processing. + */ + for (const key in latestValues) { + const value = latestValues[key]; + if (motionDom.transformProps.has(key)) { + // If this is a transform, flag to enable further transform processing + hasTransform = true; + continue; + } + else if (motionDom.isCSSVariableName(key)) { + vars[key] = value; + continue; + } + else { + // Convert the value to its default value type, ie 0 -> "0px" + const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]); + if (key.startsWith("origin")) { + // If this is a transform origin, flag and enable further transform-origin processing + hasTransformOrigin = true; + transformOrigin[key] = + valueAsType; + } + else { + style[key] = valueAsType; + } + } + } + if (!latestValues.transform) { + if (hasTransform || transformTemplate) { + style.transform = buildTransform(latestValues, state.transform, transformTemplate); + } + else if (style.transform) { + /** + * If we have previously created a transform but currently don't have any, + * reset transform style to none. + */ + style.transform = "none"; + } + } + /** + * Build a transformOrigin style. Uses the same defaults as the browser for + * undefined origins. + */ + if (hasTransformOrigin) { + const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin; + style.transformOrigin = `${originX} ${originY} ${originZ}`; + } +} + +function renderHTML(element, { style, vars }, styleProp, projection) { + const elementStyle = element.style; + let key; + for (key in style) { + // CSSStyleDeclaration has [index: number]: string; in the types, so we use that as key type. + elementStyle[key] = style[key]; + } + // Write projection styles directly to element style + projection?.applyProjectionStyles(elementStyle, styleProp); + for (key in vars) { + // Loop over any CSS variables and assign those. + // They can only be assigned using `setProperty`. + elementStyle.setProperty(key, vars[key]); + } +} + +function isForcedMotionValue(key, { layout, layoutId }) { + return (motionDom.transformProps.has(key) || + key.startsWith("origin") || + ((layout || layoutId !== undefined) && + (!!scaleCorrectors[key] || key === "opacity"))); +} + +function scrapeMotionValuesFromProps$1(props, prevProps, visualElement) { + const { style } = props; + const newValues = {}; + for (const key in style) { + if (motionDom.isMotionValue(style[key]) || + (prevProps.style && + motionDom.isMotionValue(prevProps.style[key])) || + isForcedMotionValue(key, props) || + visualElement?.getValue(key)?.liveStyle !== undefined) { + newValues[key] = style[key]; + } + } + return newValues; +} + +function getComputedStyle(element) { + return window.getComputedStyle(element); +} +class HTMLVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "html"; + this.renderInstance = renderHTML; + } + readValueFromInstance(instance, key) { + if (motionDom.transformProps.has(key)) { + return this.projection?.isProjecting + ? motionDom.defaultTransformValue(key) + : motionDom.readTransformValue(instance, key); + } + else { + const computedStyle = getComputedStyle(instance); + const value = (motionDom.isCSSVariableName(key) + ? computedStyle.getPropertyValue(key) + : computedStyle[key]) || 0; + return typeof value === "string" ? value.trim() : value; + } + } + measureInstanceViewportBox(instance, { transformPagePoint }) { + return measureViewportBox(instance, transformPagePoint); + } + build(renderState, latestValues, props) { + buildHTMLStyles(renderState, latestValues, props.transformTemplate); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + } +} + +const LazyContext = React.createContext({ strict: false }); + +function loadFeatures(features) { + for (const key in features) { + featureDefinitions[key] = { + ...featureDefinitions[key], + ...features[key], + }; + } +} + +/** + * A list of all valid MotionProps. + * + * @privateRemarks + * This doesn't throw if a `MotionProp` name is missing - it should. + */ +const validMotionProps = new Set([ + "animate", + "exit", + "variants", + "initial", + "style", + "values", + "variants", + "transition", + "transformTemplate", + "custom", + "inherit", + "onBeforeLayoutMeasure", + "onAnimationStart", + "onAnimationComplete", + "onUpdate", + "onDragStart", + "onDrag", + "onDragEnd", + "onMeasureDragConstraints", + "onDirectionLock", + "onDragTransitionEnd", + "_dragX", + "_dragY", + "onHoverStart", + "onHoverEnd", + "onViewportEnter", + "onViewportLeave", + "globalTapTarget", + "ignoreStrict", + "viewport", +]); +/** + * Check whether a prop name is a valid `MotionProp` key. + * + * @param key - Name of the property to check + * @returns `true` is key is a valid `MotionProp`. + * + * @public + */ +function isValidMotionProp(key) { + return (key.startsWith("while") || + (key.startsWith("drag") && key !== "draggable") || + key.startsWith("layout") || + key.startsWith("onTap") || + key.startsWith("onPan") || + key.startsWith("onLayout") || + validMotionProps.has(key)); +} + +let shouldForward = (key) => !isValidMotionProp(key); +function loadExternalIsValidProp(isValidProp) { + if (typeof isValidProp !== "function") + return; + // Explicitly filter our events + shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key); +} +/** + * Emotion and Styled Components both allow users to pass through arbitrary props to their components + * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which + * of these should be passed to the underlying DOM node. + * + * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props + * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props + * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of + * `@emotion/is-prop-valid`, however to fix this problem we need to use it. + * + * By making it an optionalDependency we can offer this functionality only in the situations where it's + * actually required. + */ +try { + /** + * We attempt to import this package but require won't be defined in esm environments, in that case + * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed + * in favour of explicit injection. + */ + loadExternalIsValidProp(require("@emotion/is-prop-valid").default); +} +catch { + // We don't need to actually do anything here - the fallback is the existing `isPropValid`. +} +function filterProps(props, isDom, forwardMotionProps) { + const filteredProps = {}; + for (const key in props) { + /** + * values is considered a valid prop by Emotion, so if it's present + * this will be rendered out to the DOM unless explicitly filtered. + * + * We check the type as it could be used with the `feColorMatrix` + * element, which we support. + */ + if (key === "values" && typeof props.values === "object") + continue; + if (shouldForward(key) || + (forwardMotionProps === true && isValidMotionProp(key)) || + (!isDom && !isValidMotionProp(key)) || + // If trying to use native HTML drag events, forward drag listeners + (props["draggable"] && + key.startsWith("onDrag"))) { + filteredProps[key] = + props[key]; + } + } + return filteredProps; +} + +function resolveVariant(visualElement, definition, custom) { + const props = visualElement.getProps(); + return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement); +} + +const isKeyframesTarget = (v) => { + return Array.isArray(v); +}; + +/** + * Set VisualElement's MotionValue, creating a new MotionValue for it if + * it doesn't exist. + */ +function setMotionValue(visualElement, key, value) { + if (visualElement.hasValue(key)) { + visualElement.getValue(key).set(value); + } + else { + visualElement.addValue(key, motionDom.motionValue(value)); + } +} +function resolveFinalValueInKeyframes(v) { + // TODO maybe throw if v.length - 1 is placeholder token? + return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; +} +function setTarget(visualElement, definition) { + const resolved = resolveVariant(visualElement, definition); + let { transitionEnd = {}, transition = {}, ...target } = resolved || {}; + target = { ...target, ...transitionEnd }; + for (const key in target) { + const value = resolveFinalValueInKeyframes(target[key]); + setMotionValue(visualElement, key, value); + } +} + +function isWillChangeMotionValue(value) { + return Boolean(motionDom.isMotionValue(value) && value.add); +} + +function addValueToWillChange(visualElement, key) { + const willChange = visualElement.getValue("willChange"); + /** + * It could be that a user has set willChange to a regular MotionValue, + * in which case we can't add the value to it. + */ + if (isWillChangeMotionValue(willChange)) { + return willChange.add(key); + } + else if (!willChange && motionUtils.MotionGlobalConfig.WillChange) { + const newWillChange = new motionUtils.MotionGlobalConfig.WillChange("auto"); + visualElement.addValue("willChange", newWillChange); + newWillChange.add(key); + } +} + +/** + * Decide whether we should block this animation. Previously, we achieved this + * just by checking whether the key was listed in protectedKeys, but this + * posed problems if an animation was triggered by afterChildren and protectedKeys + * had been set to true in the meantime. + */ +function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) { + const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true; + needsAnimating[key] = false; + return shouldBlock; +} +function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) { + let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition; + if (transitionOverride) + transition = transitionOverride; + const animations = []; + const animationTypeState = type && + visualElement.animationState && + visualElement.animationState.getState()[type]; + for (const key in target) { + const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null); + const valueTarget = target[key]; + if (valueTarget === undefined || + (animationTypeState && + shouldBlockAnimation(animationTypeState, key))) { + continue; + } + const valueTransition = { + delay, + ...motionDom.getValueTransition(transition || {}, key), + }; + /** + * If the value is already at the defined target, skip the animation. + */ + const currentValue = value.get(); + if (currentValue !== undefined && + !value.isAnimating && + !Array.isArray(valueTarget) && + valueTarget === currentValue && + !valueTransition.velocity) { + continue; + } + /** + * If this is the first time a value is being animated, check + * to see if we're handling off from an existing animation. + */ + let isHandoff = false; + if (window.MotionHandoffAnimation) { + const appearId = getOptimisedAppearId(visualElement); + if (appearId) { + const startTime = window.MotionHandoffAnimation(appearId, key, motionDom.frame); + if (startTime !== null) { + valueTransition.startTime = startTime; + isHandoff = true; + } + } + } + addValueToWillChange(visualElement, key); + value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && motionDom.positionalKeys.has(key) + ? { type: false } + : valueTransition, visualElement, isHandoff)); + const animation = value.animation; + if (animation) { + animations.push(animation); + } + } + if (transitionEnd) { + Promise.all(animations).then(() => { + motionDom.frame.update(() => { + transitionEnd && setTarget(visualElement, transitionEnd); + }); + }); + } + return animations; +} + +function animateVariant(visualElement, variant, options = {}) { + const resolved = resolveVariant(visualElement, variant, options.type === "exit" + ? visualElement.presenceContext?.custom + : undefined); + let { transition = visualElement.getDefaultTransition() || {} } = resolved || {}; + if (options.transitionOverride) { + transition = options.transitionOverride; + } + /** + * If we have a variant, create a callback that runs it as an animation. + * Otherwise, we resolve a Promise immediately for a composable no-op. + */ + const getAnimation = resolved + ? () => Promise.all(animateTarget(visualElement, resolved, options)) + : () => Promise.resolve(); + /** + * If we have children, create a callback that runs all their animations. + * Otherwise, we resolve a Promise immediately for a composable no-op. + */ + const getChildAnimations = visualElement.variantChildren && visualElement.variantChildren.size + ? (forwardDelay = 0) => { + const { delayChildren = 0, staggerChildren, staggerDirection, } = transition; + return animateChildren(visualElement, variant, forwardDelay, delayChildren, staggerChildren, staggerDirection, options); + } + : () => Promise.resolve(); + /** + * If the transition explicitly defines a "when" option, we need to resolve either + * this animation or all children animations before playing the other. + */ + const { when } = transition; + if (when) { + const [first, last] = when === "beforeChildren" + ? [getAnimation, getChildAnimations] + : [getChildAnimations, getAnimation]; + return first().then(() => last()); + } + else { + return Promise.all([getAnimation(), getChildAnimations(options.delay)]); + } +} +function animateChildren(visualElement, variant, delay = 0, delayChildren = 0, staggerChildren = 0, staggerDirection = 1, options) { + const animations = []; + const numChildren = visualElement.variantChildren.size; + const maxStaggerDuration = (numChildren - 1) * staggerChildren; + const delayIsFunction = typeof delayChildren === "function"; + const generateStaggerDuration = delayIsFunction + ? (i) => delayChildren(i, numChildren) + : // Support deprecated staggerChildren + staggerDirection === 1 + ? (i = 0) => i * staggerChildren + : (i = 0) => maxStaggerDuration - i * staggerChildren; + Array.from(visualElement.variantChildren) + .sort(sortByTreeOrder) + .forEach((child, i) => { + child.notify("AnimationStart", variant); + animations.push(animateVariant(child, variant, { + ...options, + delay: delay + + (delayIsFunction ? 0 : delayChildren) + + generateStaggerDuration(i), + }).then(() => child.notify("AnimationComplete", variant))); + }); + return Promise.all(animations); +} +function sortByTreeOrder(a, b) { + return a.sortNodePosition(b); +} + +function animateVisualElement(visualElement, definition, options = {}) { + visualElement.notify("AnimationStart", definition); + let animation; + if (Array.isArray(definition)) { + const animations = definition.map((variant) => animateVariant(visualElement, variant, options)); + animation = Promise.all(animations); + } + else if (typeof definition === "string") { + animation = animateVariant(visualElement, definition, options); + } + else { + const resolvedDefinition = typeof definition === "function" + ? resolveVariant(visualElement, definition, options.custom) + : definition; + animation = Promise.all(animateTarget(visualElement, resolvedDefinition, options)); + } + return animation.then(() => { + visualElement.notify("AnimationComplete", definition); + }); +} + +function shallowCompare(next, prev) { + if (!Array.isArray(prev)) + return false; + const prevLength = prev.length; + if (prevLength !== next.length) + return false; + for (let i = 0; i < prevLength; i++) { + if (prev[i] !== next[i]) + return false; + } + return true; +} + +const numVariantProps = variantProps.length; +function getVariantContext(visualElement) { + if (!visualElement) + return undefined; + if (!visualElement.isControllingVariants) { + const context = visualElement.parent + ? getVariantContext(visualElement.parent) || {} + : {}; + if (visualElement.props.initial !== undefined) { + context.initial = visualElement.props.initial; + } + return context; + } + const context = {}; + for (let i = 0; i < numVariantProps; i++) { + const name = variantProps[i]; + const prop = visualElement.props[name]; + if (isVariantLabel(prop) || prop === false) { + context[name] = prop; + } + } + return context; +} + +const reversePriorityOrder = [...variantPriorityOrder].reverse(); +const numAnimationTypes = variantPriorityOrder.length; +function animateList(visualElement) { + return (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options))); +} +function createAnimationState(visualElement) { + let animate = animateList(visualElement); + let state = createState(); + let isInitialRender = true; + /** + * This function will be used to reduce the animation definitions for + * each active animation type into an object of resolved values for it. + */ + const buildResolvedTypeValues = (type) => (acc, definition) => { + const resolved = resolveVariant(visualElement, definition, type === "exit" + ? visualElement.presenceContext?.custom + : undefined); + if (resolved) { + const { transition, transitionEnd, ...target } = resolved; + acc = { ...acc, ...target, ...transitionEnd }; + } + return acc; + }; + /** + * This just allows us to inject mocked animation functions + * @internal + */ + function setAnimateFunction(makeAnimator) { + animate = makeAnimator(visualElement); + } + /** + * When we receive new props, we need to: + * 1. Create a list of protected keys for each type. This is a directory of + * value keys that are currently being "handled" by types of a higher priority + * so that whenever an animation is played of a given type, these values are + * protected from being animated. + * 2. Determine if an animation type needs animating. + * 3. Determine if any values have been removed from a type and figure out + * what to animate those to. + */ + function animateChanges(changedActiveType) { + const { props } = visualElement; + const context = getVariantContext(visualElement.parent) || {}; + /** + * A list of animations that we'll build into as we iterate through the animation + * types. This will get executed at the end of the function. + */ + const animations = []; + /** + * Keep track of which values have been removed. Then, as we hit lower priority + * animation types, we can check if they contain removed values and animate to that. + */ + const removedKeys = new Set(); + /** + * A dictionary of all encountered keys. This is an object to let us build into and + * copy it without iteration. Each time we hit an animation type we set its protected + * keys - the keys its not allowed to animate - to the latest version of this object. + */ + let encounteredKeys = {}; + /** + * If a variant has been removed at a given index, and this component is controlling + * variant animations, we want to ensure lower-priority variants are forced to animate. + */ + let removedVariantIndex = Infinity; + /** + * Iterate through all animation types in reverse priority order. For each, we want to + * detect which values it's handling and whether or not they've changed (and therefore + * need to be animated). If any values have been removed, we want to detect those in + * lower priority props and flag for animation. + */ + for (let i = 0; i < numAnimationTypes; i++) { + const type = reversePriorityOrder[i]; + const typeState = state[type]; + const prop = props[type] !== undefined + ? props[type] + : context[type]; + const propIsVariant = isVariantLabel(prop); + /** + * If this type has *just* changed isActive status, set activeDelta + * to that status. Otherwise set to null. + */ + const activeDelta = type === changedActiveType ? typeState.isActive : null; + if (activeDelta === false) + removedVariantIndex = i; + /** + * If this prop is an inherited variant, rather than been set directly on the + * component itself, we want to make sure we allow the parent to trigger animations. + * + * TODO: Can probably change this to a !isControllingVariants check + */ + let isInherited = prop === context[type] && + prop !== props[type] && + propIsVariant; + /** + * + */ + if (isInherited && + isInitialRender && + visualElement.manuallyAnimateOnMount) { + isInherited = false; + } + /** + * Set all encountered keys so far as the protected keys for this type. This will + * be any key that has been animated or otherwise handled by active, higher-priortiy types. + */ + typeState.protectedKeys = { ...encounteredKeys }; + // Check if we can skip analysing this prop early + if ( + // If it isn't active and hasn't *just* been set as inactive + (!typeState.isActive && activeDelta === null) || + // If we didn't and don't have any defined prop for this animation type + (!prop && !typeState.prevProp) || + // Or if the prop doesn't define an animation + isAnimationControls(prop) || + typeof prop === "boolean") { + continue; + } + /** + * As we go look through the values defined on this type, if we detect + * a changed value or a value that was removed in a higher priority, we set + * this to true and add this prop to the animation list. + */ + const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop); + let shouldAnimateType = variantDidChange || + // If we're making this variant active, we want to always make it active + (type === changedActiveType && + typeState.isActive && + !isInherited && + propIsVariant) || + // If we removed a higher-priority variant (i is in reverse order) + (i > removedVariantIndex && propIsVariant); + let handledRemovedValues = false; + /** + * As animations can be set as variant lists, variants or target objects, we + * coerce everything to an array if it isn't one already + */ + const definitionList = Array.isArray(prop) ? prop : [prop]; + /** + * Build an object of all the resolved values. We'll use this in the subsequent + * animateChanges calls to determine whether a value has changed. + */ + let resolvedValues = definitionList.reduce(buildResolvedTypeValues(type), {}); + if (activeDelta === false) + resolvedValues = {}; + /** + * Now we need to loop through all the keys in the prev prop and this prop, + * and decide: + * 1. If the value has changed, and needs animating + * 2. If it has been removed, and needs adding to the removedKeys set + * 3. If it has been removed in a higher priority type and needs animating + * 4. If it hasn't been removed in a higher priority but hasn't changed, and + * needs adding to the type's protectedKeys list. + */ + const { prevResolvedValues = {} } = typeState; + const allKeys = { + ...prevResolvedValues, + ...resolvedValues, + }; + const markToAnimate = (key) => { + shouldAnimateType = true; + if (removedKeys.has(key)) { + handledRemovedValues = true; + removedKeys.delete(key); + } + typeState.needsAnimating[key] = true; + const motionValue = visualElement.getValue(key); + if (motionValue) + motionValue.liveStyle = false; + }; + for (const key in allKeys) { + const next = resolvedValues[key]; + const prev = prevResolvedValues[key]; + // If we've already handled this we can just skip ahead + if (encounteredKeys.hasOwnProperty(key)) + continue; + /** + * If the value has changed, we probably want to animate it. + */ + let valueHasChanged = false; + if (isKeyframesTarget(next) && isKeyframesTarget(prev)) { + valueHasChanged = !shallowCompare(next, prev); + } + else { + valueHasChanged = next !== prev; + } + if (valueHasChanged) { + if (next !== undefined && next !== null) { + // If next is defined and doesn't equal prev, it needs animating + markToAnimate(key); + } + else { + // If it's undefined, it's been removed. + removedKeys.add(key); + } + } + else if (next !== undefined && removedKeys.has(key)) { + /** + * If next hasn't changed and it isn't undefined, we want to check if it's + * been removed by a higher priority + */ + markToAnimate(key); + } + else { + /** + * If it hasn't changed, we add it to the list of protected values + * to ensure it doesn't get animated. + */ + typeState.protectedKeys[key] = true; + } + } + /** + * Update the typeState so next time animateChanges is called we can compare the + * latest prop and resolvedValues to these. + */ + typeState.prevProp = prop; + typeState.prevResolvedValues = resolvedValues; + /** + * + */ + if (typeState.isActive) { + encounteredKeys = { ...encounteredKeys, ...resolvedValues }; + } + if (isInitialRender && visualElement.blockInitialAnimation) { + shouldAnimateType = false; + } + /** + * If this is an inherited prop we want to skip this animation + * unless the inherited variants haven't changed on this render. + */ + const willAnimateViaParent = isInherited && variantDidChange; + const needsAnimating = !willAnimateViaParent || handledRemovedValues; + if (shouldAnimateType && needsAnimating) { + animations.push(...definitionList.map((animation) => ({ + animation: animation, + options: { type }, + }))); + } + } + /** + * If there are some removed value that haven't been dealt with, + * we need to create a new animation that falls back either to the value + * defined in the style prop, or the last read value. + */ + if (removedKeys.size) { + const fallbackAnimation = {}; + /** + * If the initial prop contains a transition we can use that, otherwise + * allow the animation function to use the visual element's default. + */ + if (typeof props.initial !== "boolean") { + const initialTransition = resolveVariant(visualElement, Array.isArray(props.initial) + ? props.initial[0] + : props.initial); + if (initialTransition && initialTransition.transition) { + fallbackAnimation.transition = initialTransition.transition; + } + } + removedKeys.forEach((key) => { + const fallbackTarget = visualElement.getBaseTarget(key); + const motionValue = visualElement.getValue(key); + if (motionValue) + motionValue.liveStyle = true; + // @ts-expect-error - @mattgperry to figure if we should do something here + fallbackAnimation[key] = fallbackTarget ?? null; + }); + animations.push({ animation: fallbackAnimation }); + } + let shouldAnimate = Boolean(animations.length); + if (isInitialRender && + (props.initial === false || props.initial === props.animate) && + !visualElement.manuallyAnimateOnMount) { + shouldAnimate = false; + } + isInitialRender = false; + return shouldAnimate ? animate(animations) : Promise.resolve(); + } + /** + * Change whether a certain animation type is active. + */ + function setActive(type, isActive) { + // If the active state hasn't changed, we can safely do nothing here + if (state[type].isActive === isActive) + return Promise.resolve(); + // Propagate active change to children + visualElement.variantChildren?.forEach((child) => child.animationState?.setActive(type, isActive)); + state[type].isActive = isActive; + const animations = animateChanges(type); + for (const key in state) { + state[key].protectedKeys = {}; + } + return animations; + } + return { + animateChanges, + setActive, + setAnimateFunction, + getState: () => state, + reset: () => { + state = createState(); + isInitialRender = true; + }, + }; +} +function checkVariantsDidChange(prev, next) { + if (typeof next === "string") { + return next !== prev; + } + else if (Array.isArray(next)) { + return !shallowCompare(next, prev); + } + return false; +} +function createTypeState(isActive = false) { + return { + isActive, + protectedKeys: {}, + needsAnimating: {}, + prevResolvedValues: {}, + }; +} +function createState() { + return { + animate: createTypeState(true), + whileInView: createTypeState(), + whileHover: createTypeState(), + whileTap: createTypeState(), + whileDrag: createTypeState(), + whileFocus: createTypeState(), + exit: createTypeState(), + }; +} + +class Feature { + constructor(node) { + this.isMounted = false; + this.node = node; + } + update() { } +} + +class AnimationFeature extends Feature { + /** + * We dynamically generate the AnimationState manager as it contains a reference + * to the underlying animation library. We only want to load that if we load this, + * so people can optionally code split it out using the `m` component. + */ + constructor(node) { + super(node); + node.animationState || (node.animationState = createAnimationState(node)); + } + updateAnimationControlsSubscription() { + const { animate } = this.node.getProps(); + if (isAnimationControls(animate)) { + this.unmountControls = animate.subscribe(this.node); + } + } + /** + * Subscribe any provided AnimationControls to the component's VisualElement + */ + mount() { + this.updateAnimationControlsSubscription(); + } + update() { + const { animate } = this.node.getProps(); + const { animate: prevAnimate } = this.node.prevProps || {}; + if (animate !== prevAnimate) { + this.updateAnimationControlsSubscription(); + } + } + unmount() { + this.node.animationState.reset(); + this.unmountControls?.(); + } +} + +let id = 0; +class ExitAnimationFeature extends Feature { + constructor() { + super(...arguments); + this.id = id++; + } + update() { + if (!this.node.presenceContext) + return; + const { isPresent, onExitComplete } = this.node.presenceContext; + const { isPresent: prevIsPresent } = this.node.prevPresenceContext || {}; + if (!this.node.animationState || isPresent === prevIsPresent) { + return; + } + const exitAnimation = this.node.animationState.setActive("exit", !isPresent); + if (onExitComplete && !isPresent) { + exitAnimation.then(() => { + onExitComplete(this.id); + }); + } + } + mount() { + const { register, onExitComplete } = this.node.presenceContext || {}; + if (onExitComplete) { + onExitComplete(this.id); + } + if (register) { + this.unmount = register(this.id); + } + } + unmount() { } +} + +const animations = { + animation: { + Feature: AnimationFeature, + }, + exit: { + Feature: ExitAnimationFeature, + }, +}; + +function extractEventInfo(event) { + return { + point: { + x: event.pageX, + y: event.pageY, + }, + }; +} +const addPointerInfo = (handler) => { + return (event) => motionDom.isPrimaryPointer(event) && handler(event, extractEventInfo(event)); +}; + +function addPointerEvent(target, eventName, handler, options) { + return addDomEvent(target, eventName, addPointerInfo(handler), options); +} + +// Fixes https://github.com/motiondivision/motion/issues/2270 +const getContextWindow = ({ current }) => { + return current ? current.ownerDocument.defaultView : null; +}; + +function isRefObject(ref) { + return (ref && + typeof ref === "object" && + Object.prototype.hasOwnProperty.call(ref, "current")); +} + +const distance = (a, b) => Math.abs(a - b); +function distance2D(a, b) { + // Multi-dimensional + const xDelta = distance(a.x, b.x); + const yDelta = distance(a.y, b.y); + return Math.sqrt(xDelta ** 2 + yDelta ** 2); +} + +/** + * @internal + */ +class PanSession { + constructor(event, handlers, { transformPagePoint, contextWindow = window, dragSnapToOrigin = false, distanceThreshold = 3, } = {}) { + /** + * @internal + */ + this.startEvent = null; + /** + * @internal + */ + this.lastMoveEvent = null; + /** + * @internal + */ + this.lastMoveEventInfo = null; + /** + * @internal + */ + this.handlers = {}; + /** + * @internal + */ + this.contextWindow = window; + this.updatePoint = () => { + if (!(this.lastMoveEvent && this.lastMoveEventInfo)) + return; + const info = getPanInfo(this.lastMoveEventInfo, this.history); + const isPanStarted = this.startEvent !== null; + // Only start panning if the offset is larger than 3 pixels. If we make it + // any larger than this we'll want to reset the pointer history + // on the first update to avoid visual snapping to the cursor. + const isDistancePastThreshold = distance2D(info.offset, { x: 0, y: 0 }) >= this.distanceThreshold; + if (!isPanStarted && !isDistancePastThreshold) + return; + const { point } = info; + const { timestamp } = motionDom.frameData; + this.history.push({ ...point, timestamp }); + const { onStart, onMove } = this.handlers; + if (!isPanStarted) { + onStart && onStart(this.lastMoveEvent, info); + this.startEvent = this.lastMoveEvent; + } + onMove && onMove(this.lastMoveEvent, info); + }; + this.handlePointerMove = (event, info) => { + this.lastMoveEvent = event; + this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint); + // Throttle mouse move event to once per frame + motionDom.frame.update(this.updatePoint, true); + }; + this.handlePointerUp = (event, info) => { + this.end(); + const { onEnd, onSessionEnd, resumeAnimation } = this.handlers; + if (this.dragSnapToOrigin) + resumeAnimation && resumeAnimation(); + if (!(this.lastMoveEvent && this.lastMoveEventInfo)) + return; + const panInfo = getPanInfo(event.type === "pointercancel" + ? this.lastMoveEventInfo + : transformPoint(info, this.transformPagePoint), this.history); + if (this.startEvent && onEnd) { + onEnd(event, panInfo); + } + onSessionEnd && onSessionEnd(event, panInfo); + }; + // If we have more than one touch, don't start detecting this gesture + if (!motionDom.isPrimaryPointer(event)) + return; + this.dragSnapToOrigin = dragSnapToOrigin; + this.handlers = handlers; + this.transformPagePoint = transformPagePoint; + this.distanceThreshold = distanceThreshold; + this.contextWindow = contextWindow || window; + const info = extractEventInfo(event); + const initialInfo = transformPoint(info, this.transformPagePoint); + const { point } = initialInfo; + const { timestamp } = motionDom.frameData; + this.history = [{ ...point, timestamp }]; + const { onSessionStart } = handlers; + onSessionStart && + onSessionStart(event, getPanInfo(initialInfo, this.history)); + this.removeListeners = motionUtils.pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp)); + } + updateHandlers(handlers) { + this.handlers = handlers; + } + end() { + this.removeListeners && this.removeListeners(); + motionDom.cancelFrame(this.updatePoint); + } +} +function transformPoint(info, transformPagePoint) { + return transformPagePoint ? { point: transformPagePoint(info.point) } : info; +} +function subtractPoint(a, b) { + return { x: a.x - b.x, y: a.y - b.y }; +} +function getPanInfo({ point }, history) { + return { + point, + delta: subtractPoint(point, lastDevicePoint(history)), + offset: subtractPoint(point, startDevicePoint(history)), + velocity: getVelocity(history, 0.1), + }; +} +function startDevicePoint(history) { + return history[0]; +} +function lastDevicePoint(history) { + return history[history.length - 1]; +} +function getVelocity(history, timeDelta) { + if (history.length < 2) { + return { x: 0, y: 0 }; + } + let i = history.length - 1; + let timestampedPoint = null; + const lastPoint = lastDevicePoint(history); + while (i >= 0) { + timestampedPoint = history[i]; + if (lastPoint.timestamp - timestampedPoint.timestamp > + motionUtils.secondsToMilliseconds(timeDelta)) { + break; + } + i--; + } + if (!timestampedPoint) { + return { x: 0, y: 0 }; + } + const time = motionUtils.millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp); + if (time === 0) { + return { x: 0, y: 0 }; + } + const currentVelocity = { + x: (lastPoint.x - timestampedPoint.x) / time, + y: (lastPoint.y - timestampedPoint.y) / time, + }; + if (currentVelocity.x === Infinity) { + currentVelocity.x = 0; + } + if (currentVelocity.y === Infinity) { + currentVelocity.y = 0; + } + return currentVelocity; +} + +/** + * Apply constraints to a point. These constraints are both physical along an + * axis, and an elastic factor that determines how much to constrain the point + * by if it does lie outside the defined parameters. + */ +function applyConstraints(point, { min, max }, elastic) { + if (min !== undefined && point < min) { + // If we have a min point defined, and this is outside of that, constrain + point = elastic + ? motionDom.mixNumber(min, point, elastic.min) + : Math.max(point, min); + } + else if (max !== undefined && point > max) { + // If we have a max point defined, and this is outside of that, constrain + point = elastic + ? motionDom.mixNumber(max, point, elastic.max) + : Math.min(point, max); + } + return point; +} +/** + * Calculate constraints in terms of the viewport when defined relatively to the + * measured axis. This is measured from the nearest edge, so a max constraint of 200 + * on an axis with a max value of 300 would return a constraint of 500 - axis length + */ +function calcRelativeAxisConstraints(axis, min, max) { + return { + min: min !== undefined ? axis.min + min : undefined, + max: max !== undefined + ? axis.max + max - (axis.max - axis.min) + : undefined, + }; +} +/** + * Calculate constraints in terms of the viewport when + * defined relatively to the measured bounding box. + */ +function calcRelativeConstraints(layoutBox, { top, left, bottom, right }) { + return { + x: calcRelativeAxisConstraints(layoutBox.x, left, right), + y: calcRelativeAxisConstraints(layoutBox.y, top, bottom), + }; +} +/** + * Calculate viewport constraints when defined as another viewport-relative axis + */ +function calcViewportAxisConstraints(layoutAxis, constraintsAxis) { + let min = constraintsAxis.min - layoutAxis.min; + let max = constraintsAxis.max - layoutAxis.max; + // If the constraints axis is actually smaller than the layout axis then we can + // flip the constraints + if (constraintsAxis.max - constraintsAxis.min < + layoutAxis.max - layoutAxis.min) { + [min, max] = [max, min]; + } + return { min, max }; +} +/** + * Calculate viewport constraints when defined as another viewport-relative box + */ +function calcViewportConstraints(layoutBox, constraintsBox) { + return { + x: calcViewportAxisConstraints(layoutBox.x, constraintsBox.x), + y: calcViewportAxisConstraints(layoutBox.y, constraintsBox.y), + }; +} +/** + * Calculate a transform origin relative to the source axis, between 0-1, that results + * in an asthetically pleasing scale/transform needed to project from source to target. + */ +function calcOrigin(source, target) { + let origin = 0.5; + const sourceLength = calcLength(source); + const targetLength = calcLength(target); + if (targetLength > sourceLength) { + origin = motionUtils.progress(target.min, target.max - sourceLength, source.min); + } + else if (sourceLength > targetLength) { + origin = motionUtils.progress(source.min, source.max - targetLength, target.min); + } + return motionUtils.clamp(0, 1, origin); +} +/** + * Rebase the calculated viewport constraints relative to the layout.min point. + */ +function rebaseAxisConstraints(layout, constraints) { + const relativeConstraints = {}; + if (constraints.min !== undefined) { + relativeConstraints.min = constraints.min - layout.min; + } + if (constraints.max !== undefined) { + relativeConstraints.max = constraints.max - layout.min; + } + return relativeConstraints; +} +const defaultElastic = 0.35; +/** + * Accepts a dragElastic prop and returns resolved elastic values for each axis. + */ +function resolveDragElastic(dragElastic = defaultElastic) { + if (dragElastic === false) { + dragElastic = 0; + } + else if (dragElastic === true) { + dragElastic = defaultElastic; + } + return { + x: resolveAxisElastic(dragElastic, "left", "right"), + y: resolveAxisElastic(dragElastic, "top", "bottom"), + }; +} +function resolveAxisElastic(dragElastic, minLabel, maxLabel) { + return { + min: resolvePointElastic(dragElastic, minLabel), + max: resolvePointElastic(dragElastic, maxLabel), + }; +} +function resolvePointElastic(dragElastic, label) { + return typeof dragElastic === "number" + ? dragElastic + : dragElastic[label] || 0; +} + +const elementDragControls = new WeakMap(); +class VisualElementDragControls { + constructor(visualElement) { + this.openDragLock = null; + this.isDragging = false; + this.currentDirection = null; + this.originPoint = { x: 0, y: 0 }; + /** + * The permitted boundaries of travel, in pixels. + */ + this.constraints = false; + this.hasMutatedConstraints = false; + /** + * The per-axis resolved elastic values. + */ + this.elastic = createBox(); + /** + * The latest pointer event. Used as fallback when the `cancel` and `stop` functions are called without arguments. + */ + this.latestPointerEvent = null; + /** + * The latest pan info. Used as fallback when the `cancel` and `stop` functions are called without arguments. + */ + this.latestPanInfo = null; + this.visualElement = visualElement; + } + start(originEvent, { snapToCursor = false, distanceThreshold } = {}) { + /** + * Don't start dragging if this component is exiting + */ + const { presenceContext } = this.visualElement; + if (presenceContext && presenceContext.isPresent === false) + return; + const onSessionStart = (event) => { + const { dragSnapToOrigin } = this.getProps(); + // Stop or pause any animations on both axis values immediately. This allows the user to throw and catch + // the component. + dragSnapToOrigin ? this.pauseAnimation() : this.stopAnimation(); + if (snapToCursor) { + this.snapToCursor(extractEventInfo(event).point); + } + }; + const onStart = (event, info) => { + // Attempt to grab the global drag gesture lock - maybe make this part of PanSession + const { drag, dragPropagation, onDragStart } = this.getProps(); + if (drag && !dragPropagation) { + if (this.openDragLock) + this.openDragLock(); + this.openDragLock = motionDom.setDragLock(drag); + // If we don 't have the lock, don't start dragging + if (!this.openDragLock) + return; + } + this.latestPointerEvent = event; + this.latestPanInfo = info; + this.isDragging = true; + this.currentDirection = null; + this.resolveConstraints(); + if (this.visualElement.projection) { + this.visualElement.projection.isAnimationBlocked = true; + this.visualElement.projection.target = undefined; + } + /** + * Record gesture origin + */ + eachAxis((axis) => { + let current = this.getAxisMotionValue(axis).get() || 0; + /** + * If the MotionValue is a percentage value convert to px + */ + if (motionDom.percent.test(current)) { + const { projection } = this.visualElement; + if (projection && projection.layout) { + const measuredAxis = projection.layout.layoutBox[axis]; + if (measuredAxis) { + const length = calcLength(measuredAxis); + current = length * (parseFloat(current) / 100); + } + } + } + this.originPoint[axis] = current; + }); + // Fire onDragStart event + if (onDragStart) { + motionDom.frame.postRender(() => onDragStart(event, info)); + } + addValueToWillChange(this.visualElement, "transform"); + const { animationState } = this.visualElement; + animationState && animationState.setActive("whileDrag", true); + }; + const onMove = (event, info) => { + this.latestPointerEvent = event; + this.latestPanInfo = info; + const { dragPropagation, dragDirectionLock, onDirectionLock, onDrag, } = this.getProps(); + // If we didn't successfully receive the gesture lock, early return. + if (!dragPropagation && !this.openDragLock) + return; + const { offset } = info; + // Attempt to detect drag direction if directionLock is true + if (dragDirectionLock && this.currentDirection === null) { + this.currentDirection = getCurrentDirection(offset); + // If we've successfully set a direction, notify listener + if (this.currentDirection !== null) { + onDirectionLock && onDirectionLock(this.currentDirection); + } + return; + } + // Update each point with the latest position + this.updateAxis("x", info.point, offset); + this.updateAxis("y", info.point, offset); + /** + * Ideally we would leave the renderer to fire naturally at the end of + * this frame but if the element is about to change layout as the result + * of a re-render we want to ensure the browser can read the latest + * bounding box to ensure the pointer and element don't fall out of sync. + */ + this.visualElement.render(); + /** + * This must fire after the render call as it might trigger a state + * change which itself might trigger a layout update. + */ + onDrag && onDrag(event, info); + }; + const onSessionEnd = (event, info) => { + this.latestPointerEvent = event; + this.latestPanInfo = info; + this.stop(event, info); + this.latestPointerEvent = null; + this.latestPanInfo = null; + }; + const resumeAnimation = () => eachAxis((axis) => this.getAnimationState(axis) === "paused" && + this.getAxisMotionValue(axis).animation?.play()); + const { dragSnapToOrigin } = this.getProps(); + this.panSession = new PanSession(originEvent, { + onSessionStart, + onStart, + onMove, + onSessionEnd, + resumeAnimation, + }, { + transformPagePoint: this.visualElement.getTransformPagePoint(), + dragSnapToOrigin, + distanceThreshold, + contextWindow: getContextWindow(this.visualElement), + }); + } + /** + * @internal + */ + stop(event, panInfo) { + const finalEvent = event || this.latestPointerEvent; + const finalPanInfo = panInfo || this.latestPanInfo; + const isDragging = this.isDragging; + this.cancel(); + if (!isDragging || !finalPanInfo || !finalEvent) + return; + const { velocity } = finalPanInfo; + this.startAnimation(velocity); + const { onDragEnd } = this.getProps(); + if (onDragEnd) { + motionDom.frame.postRender(() => onDragEnd(finalEvent, finalPanInfo)); + } + } + /** + * @internal + */ + cancel() { + this.isDragging = false; + const { projection, animationState } = this.visualElement; + if (projection) { + projection.isAnimationBlocked = false; + } + this.panSession && this.panSession.end(); + this.panSession = undefined; + const { dragPropagation } = this.getProps(); + if (!dragPropagation && this.openDragLock) { + this.openDragLock(); + this.openDragLock = null; + } + animationState && animationState.setActive("whileDrag", false); + } + updateAxis(axis, _point, offset) { + const { drag } = this.getProps(); + // If we're not dragging this axis, do an early return. + if (!offset || !shouldDrag(axis, drag, this.currentDirection)) + return; + const axisValue = this.getAxisMotionValue(axis); + let next = this.originPoint[axis] + offset[axis]; + // Apply constraints + if (this.constraints && this.constraints[axis]) { + next = applyConstraints(next, this.constraints[axis], this.elastic[axis]); + } + axisValue.set(next); + } + resolveConstraints() { + const { dragConstraints, dragElastic } = this.getProps(); + const layout = this.visualElement.projection && + !this.visualElement.projection.layout + ? this.visualElement.projection.measure(false) + : this.visualElement.projection?.layout; + const prevConstraints = this.constraints; + if (dragConstraints && isRefObject(dragConstraints)) { + if (!this.constraints) { + this.constraints = this.resolveRefConstraints(); + } + } + else { + if (dragConstraints && layout) { + this.constraints = calcRelativeConstraints(layout.layoutBox, dragConstraints); + } + else { + this.constraints = false; + } + } + this.elastic = resolveDragElastic(dragElastic); + /** + * If we're outputting to external MotionValues, we want to rebase the measured constraints + * from viewport-relative to component-relative. + */ + if (prevConstraints !== this.constraints && + layout && + this.constraints && + !this.hasMutatedConstraints) { + eachAxis((axis) => { + if (this.constraints !== false && + this.getAxisMotionValue(axis)) { + this.constraints[axis] = rebaseAxisConstraints(layout.layoutBox[axis], this.constraints[axis]); + } + }); + } + } + resolveRefConstraints() { + const { dragConstraints: constraints, onMeasureDragConstraints } = this.getProps(); + if (!constraints || !isRefObject(constraints)) + return false; + const constraintsElement = constraints.current; + motionUtils.invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop."); + const { projection } = this.visualElement; + // TODO + if (!projection || !projection.layout) + return false; + const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint()); + let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox); + /** + * If there's an onMeasureDragConstraints listener we call it and + * if different constraints are returned, set constraints to that + */ + if (onMeasureDragConstraints) { + const userConstraints = onMeasureDragConstraints(convertBoxToBoundingBox(measuredConstraints)); + this.hasMutatedConstraints = !!userConstraints; + if (userConstraints) { + measuredConstraints = convertBoundingBoxToBox(userConstraints); + } + } + return measuredConstraints; + } + startAnimation(velocity) { + const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps(); + const constraints = this.constraints || {}; + const momentumAnimations = eachAxis((axis) => { + if (!shouldDrag(axis, drag, this.currentDirection)) { + return; + } + let transition = (constraints && constraints[axis]) || {}; + if (dragSnapToOrigin) + transition = { min: 0, max: 0 }; + /** + * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame + * of spring animations so we should look into adding a disable spring option to `inertia`. + * We could do something here where we affect the `bounceStiffness` and `bounceDamping` + * using the value of `dragElastic`. + */ + const bounceStiffness = dragElastic ? 200 : 1000000; + const bounceDamping = dragElastic ? 40 : 10000000; + const inertia = { + type: "inertia", + velocity: dragMomentum ? velocity[axis] : 0, + bounceStiffness, + bounceDamping, + timeConstant: 750, + restDelta: 1, + restSpeed: 10, + ...dragTransition, + ...transition, + }; + // If we're not animating on an externally-provided `MotionValue` we can use the + // component's animation controls which will handle interactions with whileHover (etc), + // otherwise we just have to animate the `MotionValue` itself. + return this.startAxisValueAnimation(axis, inertia); + }); + // Run all animations and then resolve the new drag constraints. + return Promise.all(momentumAnimations).then(onDragTransitionEnd); + } + startAxisValueAnimation(axis, transition) { + const axisValue = this.getAxisMotionValue(axis); + addValueToWillChange(this.visualElement, axis); + return axisValue.start(animateMotionValue(axis, axisValue, 0, transition, this.visualElement, false)); + } + stopAnimation() { + eachAxis((axis) => this.getAxisMotionValue(axis).stop()); + } + pauseAnimation() { + eachAxis((axis) => this.getAxisMotionValue(axis).animation?.pause()); + } + getAnimationState(axis) { + return this.getAxisMotionValue(axis).animation?.state; + } + /** + * Drag works differently depending on which props are provided. + * + * - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values. + * - Otherwise, we apply the delta to the x/y motion values. + */ + getAxisMotionValue(axis) { + const dragKey = `_drag${axis.toUpperCase()}`; + const props = this.visualElement.getProps(); + const externalMotionValue = props[dragKey]; + return externalMotionValue + ? externalMotionValue + : this.visualElement.getValue(axis, (props.initial + ? props.initial[axis] + : undefined) || 0); + } + snapToCursor(point) { + eachAxis((axis) => { + const { drag } = this.getProps(); + // If we're not dragging this axis, do an early return. + if (!shouldDrag(axis, drag, this.currentDirection)) + return; + const { projection } = this.visualElement; + const axisValue = this.getAxisMotionValue(axis); + if (projection && projection.layout) { + const { min, max } = projection.layout.layoutBox[axis]; + axisValue.set(point[axis] - motionDom.mixNumber(min, max, 0.5)); + } + }); + } + /** + * When the viewport resizes we want to check if the measured constraints + * have changed and, if so, reposition the element within those new constraints + * relative to where it was before the resize. + */ + scalePositionWithinConstraints() { + if (!this.visualElement.current) + return; + const { drag, dragConstraints } = this.getProps(); + const { projection } = this.visualElement; + if (!isRefObject(dragConstraints) || !projection || !this.constraints) + return; + /** + * Stop current animations as there can be visual glitching if we try to do + * this mid-animation + */ + this.stopAnimation(); + /** + * Record the relative position of the dragged element relative to the + * constraints box and save as a progress value. + */ + const boxProgress = { x: 0, y: 0 }; + eachAxis((axis) => { + const axisValue = this.getAxisMotionValue(axis); + if (axisValue && this.constraints !== false) { + const latest = axisValue.get(); + boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]); + } + }); + /** + * Update the layout of this element and resolve the latest drag constraints + */ + const { transformTemplate } = this.visualElement.getProps(); + this.visualElement.current.style.transform = transformTemplate + ? transformTemplate({}, "") + : "none"; + projection.root && projection.root.updateScroll(); + projection.updateLayout(); + this.resolveConstraints(); + /** + * For each axis, calculate the current progress of the layout axis + * within the new constraints. + */ + eachAxis((axis) => { + if (!shouldDrag(axis, drag, null)) + return; + /** + * Calculate a new transform based on the previous box progress + */ + const axisValue = this.getAxisMotionValue(axis); + const { min, max } = this.constraints[axis]; + axisValue.set(motionDom.mixNumber(min, max, boxProgress[axis])); + }); + } + addListeners() { + if (!this.visualElement.current) + return; + elementDragControls.set(this.visualElement, this); + const element = this.visualElement.current; + /** + * Attach a pointerdown event listener on this DOM element to initiate drag tracking. + */ + const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => { + const { drag, dragListener = true } = this.getProps(); + drag && dragListener && this.start(event); + }); + const measureDragConstraints = () => { + const { dragConstraints } = this.getProps(); + if (isRefObject(dragConstraints) && dragConstraints.current) { + this.constraints = this.resolveRefConstraints(); + } + }; + const { projection } = this.visualElement; + const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints); + if (projection && !projection.layout) { + projection.root && projection.root.updateScroll(); + projection.updateLayout(); + } + motionDom.frame.read(measureDragConstraints); + /** + * Attach a window resize listener to scale the draggable target within its defined + * constraints as the window resizes. + */ + const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints()); + /** + * If the element's layout changes, calculate the delta and apply that to + * the drag gesture's origin point. + */ + const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => { + if (this.isDragging && hasLayoutChanged) { + eachAxis((axis) => { + const motionValue = this.getAxisMotionValue(axis); + if (!motionValue) + return; + this.originPoint[axis] += delta[axis].translate; + motionValue.set(motionValue.get() + delta[axis].translate); + }); + this.visualElement.render(); + } + })); + return () => { + stopResizeListener(); + stopPointerListener(); + stopMeasureLayoutListener(); + stopLayoutUpdateListener && stopLayoutUpdateListener(); + }; + } + getProps() { + const props = this.visualElement.getProps(); + const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props; + return { + ...props, + drag, + dragDirectionLock, + dragPropagation, + dragConstraints, + dragElastic, + dragMomentum, + }; + } +} +function shouldDrag(direction, drag, currentDirection) { + return ((drag === true || drag === direction) && + (currentDirection === null || currentDirection === direction)); +} +/** + * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower + * than the provided threshold, return `null`. + * + * @param offset - The x/y offset from origin. + * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction. + */ +function getCurrentDirection(offset, lockThreshold = 10) { + let direction = null; + if (Math.abs(offset.y) > lockThreshold) { + direction = "y"; + } + else if (Math.abs(offset.x) > lockThreshold) { + direction = "x"; + } + return direction; +} + +class DragGesture extends Feature { + constructor(node) { + super(node); + this.removeGroupControls = motionUtils.noop; + this.removeListeners = motionUtils.noop; + this.controls = new VisualElementDragControls(node); + } + mount() { + // If we've been provided a DragControls for manual control over the drag gesture, + // subscribe this component to it on mount. + const { dragControls } = this.node.getProps(); + if (dragControls) { + this.removeGroupControls = dragControls.subscribe(this.controls); + } + this.removeListeners = this.controls.addListeners() || motionUtils.noop; + } + unmount() { + this.removeGroupControls(); + this.removeListeners(); + } +} + +const asyncHandler = (handler) => (event, info) => { + if (handler) { + motionDom.frame.postRender(() => handler(event, info)); + } +}; +class PanGesture extends Feature { + constructor() { + super(...arguments); + this.removePointerDownListener = motionUtils.noop; + } + onPointerDown(pointerDownEvent) { + this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), { + transformPagePoint: this.node.getTransformPagePoint(), + contextWindow: getContextWindow(this.node), + }); + } + createPanHandlers() { + const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps(); + return { + onSessionStart: asyncHandler(onPanSessionStart), + onStart: asyncHandler(onPanStart), + onMove: onPan, + onEnd: (event, info) => { + delete this.session; + if (onPanEnd) { + motionDom.frame.postRender(() => onPanEnd(event, info)); + } + }, + }; + } + mount() { + this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event)); + } + update() { + this.session && this.session.updateHandlers(this.createPanHandlers()); + } + unmount() { + this.removePointerDownListener(); + this.session && this.session.end(); + } +} + +/** + * Internal, exported only for usage in Framer + */ +const SwitchLayoutGroupContext = React.createContext({}); + +/** + * Track whether we've taken any snapshots yet. If not, + * we can safely skip notification of didUpdate. + */ +let hasTakenAnySnapshot = false; +class MeasureLayoutWithContext extends React.Component { + /** + * This only mounts projection nodes for components that + * need measuring, we might want to do it for all components + * in order to incorporate transforms + */ + componentDidMount() { + const { visualElement, layoutGroup, switchLayoutGroup, layoutId } = this.props; + const { projection } = visualElement; + addScaleCorrector(defaultScaleCorrectors); + if (projection) { + if (layoutGroup.group) + layoutGroup.group.add(projection); + if (switchLayoutGroup && switchLayoutGroup.register && layoutId) { + switchLayoutGroup.register(projection); + } + if (hasTakenAnySnapshot) { + projection.root.didUpdate(); + } + projection.addEventListener("animationComplete", () => { + this.safeToRemove(); + }); + projection.setOptions({ + ...projection.options, + onExitComplete: () => this.safeToRemove(), + }); + } + globalProjectionState.hasEverUpdated = true; + } + getSnapshotBeforeUpdate(prevProps) { + const { layoutDependency, visualElement, drag, isPresent } = this.props; + const { projection } = visualElement; + if (!projection) + return null; + /** + * TODO: We use this data in relegate to determine whether to + * promote a previous element. There's no guarantee its presence data + * will have updated by this point - if a bug like this arises it will + * have to be that we markForRelegation and then find a new lead some other way, + * perhaps in didUpdate + */ + projection.isPresent = isPresent; + hasTakenAnySnapshot = true; + if (drag || + prevProps.layoutDependency !== layoutDependency || + layoutDependency === undefined || + prevProps.isPresent !== isPresent) { + projection.willUpdate(); + } + else { + this.safeToRemove(); + } + if (prevProps.isPresent !== isPresent) { + if (isPresent) { + projection.promote(); + } + else if (!projection.relegate()) { + /** + * If there's another stack member taking over from this one, + * it's in charge of the exit animation and therefore should + * be in charge of the safe to remove. Otherwise we call it here. + */ + motionDom.frame.postRender(() => { + const stack = projection.getStack(); + if (!stack || !stack.members.length) { + this.safeToRemove(); + } + }); + } + } + return null; + } + componentDidUpdate() { + const { projection } = this.props.visualElement; + if (projection) { + projection.root.didUpdate(); + motionDom.microtask.postRender(() => { + if (!projection.currentAnimation && projection.isLead()) { + this.safeToRemove(); + } + }); + } + } + componentWillUnmount() { + const { visualElement, layoutGroup, switchLayoutGroup: promoteContext, } = this.props; + const { projection } = visualElement; + if (projection) { + projection.scheduleCheckAfterUnmount(); + if (layoutGroup && layoutGroup.group) + layoutGroup.group.remove(projection); + if (promoteContext && promoteContext.deregister) + promoteContext.deregister(projection); + } + } + safeToRemove() { + const { safeToRemove } = this.props; + safeToRemove && safeToRemove(); + } + render() { + return null; + } +} +function MeasureLayout(props) { + const [isPresent, safeToRemove] = usePresence(); + const layoutGroup = React.useContext(LayoutGroupContext); + return (jsxRuntime.jsx(MeasureLayoutWithContext, { ...props, layoutGroup: layoutGroup, switchLayoutGroup: React.useContext(SwitchLayoutGroupContext), isPresent: isPresent, safeToRemove: safeToRemove })); +} +const defaultScaleCorrectors = { + borderRadius: { + ...correctBorderRadius, + applyTo: [ + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomLeftRadius", + "borderBottomRightRadius", + ], + }, + borderTopLeftRadius: correctBorderRadius, + borderTopRightRadius: correctBorderRadius, + borderBottomLeftRadius: correctBorderRadius, + borderBottomRightRadius: correctBorderRadius, + boxShadow: correctBoxShadow, +}; + +const drag = { + pan: { + Feature: PanGesture, + }, + drag: { + Feature: DragGesture, + ProjectionNode: HTMLProjectionNode, + MeasureLayout, + }, +}; + +function handleHoverEvent(node, event, lifecycle) { + const { props } = node; + if (node.animationState && props.whileHover) { + node.animationState.setActive("whileHover", lifecycle === "Start"); + } + const eventName = ("onHover" + lifecycle); + const callback = props[eventName]; + if (callback) { + motionDom.frame.postRender(() => callback(event, extractEventInfo(event))); + } +} +class HoverGesture extends Feature { + mount() { + const { current } = this.node; + if (!current) + return; + this.unmount = motionDom.hover(current, (_element, startEvent) => { + handleHoverEvent(this.node, startEvent, "Start"); + return (endEvent) => handleHoverEvent(this.node, endEvent, "End"); + }); + } + unmount() { } +} + +class FocusGesture extends Feature { + constructor() { + super(...arguments); + this.isActive = false; + } + onFocus() { + let isFocusVisible = false; + /** + * If this element doesn't match focus-visible then don't + * apply whileHover. But, if matches throws that focus-visible + * is not a valid selector then in that browser outline styles will be applied + * to the element by default and we want to match that behaviour with whileFocus. + */ + try { + isFocusVisible = this.node.current.matches(":focus-visible"); + } + catch (e) { + isFocusVisible = true; + } + if (!isFocusVisible || !this.node.animationState) + return; + this.node.animationState.setActive("whileFocus", true); + this.isActive = true; + } + onBlur() { + if (!this.isActive || !this.node.animationState) + return; + this.node.animationState.setActive("whileFocus", false); + this.isActive = false; + } + mount() { + this.unmount = motionUtils.pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur())); + } + unmount() { } +} + +function handlePressEvent(node, event, lifecycle) { + const { props } = node; + if (node.current instanceof HTMLButtonElement && node.current.disabled) { + return; + } + if (node.animationState && props.whileTap) { + node.animationState.setActive("whileTap", lifecycle === "Start"); + } + const eventName = ("onTap" + (lifecycle === "End" ? "" : lifecycle)); + const callback = props[eventName]; + if (callback) { + motionDom.frame.postRender(() => callback(event, extractEventInfo(event))); + } +} +class PressGesture extends Feature { + mount() { + const { current } = this.node; + if (!current) + return; + this.unmount = motionDom.press(current, (_element, startEvent) => { + handlePressEvent(this.node, startEvent, "Start"); + return (endEvent, { success }) => handlePressEvent(this.node, endEvent, success ? "End" : "Cancel"); + }, { useGlobalTarget: this.node.props.globalTapTarget }); + } + unmount() { } +} + +/** + * Map an IntersectionHandler callback to an element. We only ever make one handler for one + * element, so even though these handlers might all be triggered by different + * observers, we can keep them in the same map. + */ +const observerCallbacks = new WeakMap(); +/** + * Multiple observers can be created for multiple element/document roots. Each with + * different settings. So here we store dictionaries of observers to each root, + * using serialised settings (threshold/margin) as lookup keys. + */ +const observers = new WeakMap(); +const fireObserverCallback = (entry) => { + const callback = observerCallbacks.get(entry.target); + callback && callback(entry); +}; +const fireAllObserverCallbacks = (entries) => { + entries.forEach(fireObserverCallback); +}; +function initIntersectionObserver({ root, ...options }) { + const lookupRoot = root || document; + /** + * If we don't have an observer lookup map for this root, create one. + */ + if (!observers.has(lookupRoot)) { + observers.set(lookupRoot, {}); + } + const rootObservers = observers.get(lookupRoot); + const key = JSON.stringify(options); + /** + * If we don't have an observer for this combination of root and settings, + * create one. + */ + if (!rootObservers[key]) { + rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, { root, ...options }); + } + return rootObservers[key]; +} +function observeIntersection(element, options, callback) { + const rootInteresectionObserver = initIntersectionObserver(options); + observerCallbacks.set(element, callback); + rootInteresectionObserver.observe(element); + return () => { + observerCallbacks.delete(element); + rootInteresectionObserver.unobserve(element); + }; +} + +const thresholdNames = { + some: 0, + all: 1, +}; +class InViewFeature extends Feature { + constructor() { + super(...arguments); + this.hasEnteredView = false; + this.isInView = false; + } + startObserver() { + this.unmount(); + const { viewport = {} } = this.node.getProps(); + const { root, margin: rootMargin, amount = "some", once } = viewport; + const options = { + root: root ? root.current : undefined, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholdNames[amount], + }; + const onIntersectionUpdate = (entry) => { + const { isIntersecting } = entry; + /** + * If there's been no change in the viewport state, early return. + */ + if (this.isInView === isIntersecting) + return; + this.isInView = isIntersecting; + /** + * Handle hasEnteredView. If this is only meant to run once, and + * element isn't visible, early return. Otherwise set hasEnteredView to true. + */ + if (once && !isIntersecting && this.hasEnteredView) { + return; + } + else if (isIntersecting) { + this.hasEnteredView = true; + } + if (this.node.animationState) { + this.node.animationState.setActive("whileInView", isIntersecting); + } + /** + * Use the latest committed props rather than the ones in scope + * when this observer is created + */ + const { onViewportEnter, onViewportLeave } = this.node.getProps(); + const callback = isIntersecting ? onViewportEnter : onViewportLeave; + callback && callback(entry); + }; + return observeIntersection(this.node.current, options, onIntersectionUpdate); + } + mount() { + this.startObserver(); + } + update() { + if (typeof IntersectionObserver === "undefined") + return; + const { props, prevProps } = this.node; + const hasOptionsChanged = ["amount", "margin", "root"].some(hasViewportOptionChanged(props, prevProps)); + if (hasOptionsChanged) { + this.startObserver(); + } + } + unmount() { } +} +function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) { + return (name) => viewport[name] !== prevViewport[name]; +} + +const gestureAnimations = { + inView: { + Feature: InViewFeature, + }, + tap: { + Feature: PressGesture, + }, + focus: { + Feature: FocusGesture, + }, + hover: { + Feature: HoverGesture, + }, +}; + +const layout = { + layout: { + ProjectionNode: HTMLProjectionNode, + MeasureLayout, + }, +}; + +const MotionContext = /* @__PURE__ */ React.createContext({}); + +function getCurrentTreeVariants(props, context) { + if (isControllingVariants(props)) { + const { initial, animate } = props; + return { + initial: initial === false || isVariantLabel(initial) + ? initial + : undefined, + animate: isVariantLabel(animate) ? animate : undefined, + }; + } + return props.inherit !== false ? context : {}; +} + +function useCreateMotionContext(props) { + const { initial, animate } = getCurrentTreeVariants(props, React.useContext(MotionContext)); + return React.useMemo(() => ({ initial, animate }), [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]); +} +function variantLabelsAsDependency(prop) { + return Array.isArray(prop) ? prop.join(" ") : prop; +} + +const motionComponentSymbol = Symbol.for("motionComponentSymbol"); + +/** + * Creates a ref function that, when called, hydrates the provided + * external ref and VisualElement. + */ +function useMotionRef(visualState, visualElement, externalRef) { + return React.useCallback((instance) => { + if (instance) { + visualState.onMount && visualState.onMount(instance); + } + if (visualElement) { + if (instance) { + visualElement.mount(instance); + } + else { + visualElement.unmount(); + } + } + if (externalRef) { + if (typeof externalRef === "function") { + externalRef(instance); + } + else if (isRefObject(externalRef)) { + externalRef.current = instance; + } + } + }, + /** + * Only pass a new ref callback to React if we've received a visual element + * factory. Otherwise we'll be mounting/remounting every time externalRef + * or other dependencies change. + */ + [visualElement]); +} + +function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor) { + const { visualElement: parent } = React.useContext(MotionContext); + const lazyContext = React.useContext(LazyContext); + const presenceContext = React.useContext(PresenceContext); + const reducedMotionConfig = React.useContext(MotionConfigContext).reducedMotion; + const visualElementRef = React.useRef(null); + /** + * If we haven't preloaded a renderer, check to see if we have one lazy-loaded + */ + createVisualElement = createVisualElement || lazyContext.renderer; + if (!visualElementRef.current && createVisualElement) { + visualElementRef.current = createVisualElement(Component, { + visualState, + parent, + props, + presenceContext, + blockInitialAnimation: presenceContext + ? presenceContext.initial === false + : false, + reducedMotionConfig, + }); + } + const visualElement = visualElementRef.current; + /** + * Load Motion gesture and animation features. These are rendered as renderless + * components so each feature can optionally make use of React lifecycle methods. + */ + const initialLayoutGroupConfig = React.useContext(SwitchLayoutGroupContext); + if (visualElement && + !visualElement.projection && + ProjectionNodeConstructor && + (visualElement.type === "html" || visualElement.type === "svg")) { + createProjectionNode(visualElementRef.current, props, ProjectionNodeConstructor, initialLayoutGroupConfig); + } + const isMounted = React.useRef(false); + React.useInsertionEffect(() => { + /** + * Check the component has already mounted before calling + * `update` unnecessarily. This ensures we skip the initial update. + */ + if (visualElement && isMounted.current) { + visualElement.update(props, presenceContext); + } + }); + /** + * Cache this value as we want to know whether HandoffAppearAnimations + * was present on initial render - it will be deleted after this. + */ + const optimisedAppearId = props[optimizedAppearDataAttribute]; + const wantsHandoff = React.useRef(Boolean(optimisedAppearId) && + !window.MotionHandoffIsComplete?.(optimisedAppearId) && + window.MotionHasOptimisedAnimation?.(optimisedAppearId)); + useIsomorphicLayoutEffect(() => { + if (!visualElement) + return; + isMounted.current = true; + window.MotionIsMounted = true; + visualElement.updateFeatures(); + motionDom.microtask.render(visualElement.render); + /** + * Ideally this function would always run in a useEffect. + * + * However, if we have optimised appear animations to handoff from, + * it needs to happen synchronously to ensure there's no flash of + * incorrect styles in the event of a hydration error. + * + * So if we detect a situtation where optimised appear animations + * are running, we use useLayoutEffect to trigger animations. + */ + if (wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + }); + React.useEffect(() => { + if (!visualElement) + return; + if (!wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + if (wantsHandoff.current) { + // This ensures all future calls to animateChanges() in this component will run in useEffect + queueMicrotask(() => { + window.MotionHandoffMarkAsComplete?.(optimisedAppearId); + }); + wantsHandoff.current = false; + } + }); + return visualElement; +} +function createProjectionNode(visualElement, props, ProjectionNodeConstructor, initialPromotionConfig) { + const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, layoutCrossfade, } = props; + visualElement.projection = new ProjectionNodeConstructor(visualElement.latestValues, props["data-framer-portal-id"] + ? undefined + : getClosestProjectingNode(visualElement.parent)); + visualElement.projection.setOptions({ + layoutId, + layout, + alwaysMeasureLayout: Boolean(drag) || (dragConstraints && isRefObject(dragConstraints)), + visualElement, + /** + * TODO: Update options in an effect. This could be tricky as it'll be too late + * to update by the time layout animations run. + * We also need to fix this safeToRemove by linking it up to the one returned by usePresence, + * ensuring it gets called if there's no potential layout animations. + * + */ + animationType: typeof layout === "string" ? layout : "both", + initialPromotionConfig, + crossfade: layoutCrossfade, + layoutScroll, + layoutRoot, + }); +} +function getClosestProjectingNode(visualElement) { + if (!visualElement) + return undefined; + return visualElement.options.allowProjection !== false + ? visualElement.projection + : getClosestProjectingNode(visualElement.parent); +} + +/** + * Create a `motion` component. + * + * This function accepts a Component argument, which can be either a string (ie "div" + * for `motion.div`), or an actual React component. + * + * Alongside this is a config option which provides a way of rendering the provided + * component "offline", or outside the React render cycle. + */ +function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) { + preloadedFeatures && loadFeatures(preloadedFeatures); + function MotionComponent(props, externalRef) { + /** + * If we need to measure the element we load this functionality in a + * separate class component in order to gain access to getSnapshotBeforeUpdate. + */ + let MeasureLayout; + const configAndProps = { + ...React.useContext(MotionConfigContext), + ...props, + layoutId: useLayoutId(props), + }; + const { isStatic } = configAndProps; + const context = useCreateMotionContext(props); + const visualState = useVisualState(props, isStatic); + if (!isStatic && isBrowser) { + useStrictMode(configAndProps, preloadedFeatures); + const layoutProjection = getProjectionFunctionality(configAndProps); + MeasureLayout = layoutProjection.MeasureLayout; + /** + * Create a VisualElement for this component. A VisualElement provides a common + * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as + * providing a way of rendering to these APIs outside of the React render loop + * for more performant animations and interactions + */ + context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode); + } + /** + * The mount order and hierarchy is specific to ensure our element ref + * is hydrated by the time features fire their effects. + */ + return (jsxRuntime.jsxs(MotionContext.Provider, { value: context, children: [MeasureLayout && context.visualElement ? (jsxRuntime.jsx(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null, useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)] })); + } + MotionComponent.displayName = `motion.${typeof Component === "string" + ? Component + : `create(${Component.displayName ?? Component.name ?? ""})`}`; + const ForwardRefMotionComponent = React.forwardRef(MotionComponent); + ForwardRefMotionComponent[motionComponentSymbol] = Component; + return ForwardRefMotionComponent; +} +function useLayoutId({ layoutId }) { + const layoutGroupId = React.useContext(LayoutGroupContext).id; + return layoutGroupId && layoutId !== undefined + ? layoutGroupId + "-" + layoutId + : layoutId; +} +function useStrictMode(configAndProps, preloadedFeatures) { + const isStrict = React.useContext(LazyContext).strict; + /** + * If we're in development mode, check to make sure we're not rendering a motion component + * as a child of LazyMotion, as this will break the file-size benefits of using it. + */ + if (process.env.NODE_ENV !== "production" && + preloadedFeatures && + isStrict) { + const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead."; + configAndProps.ignoreStrict + ? motionUtils.warning(false, strictMessage) + : motionUtils.invariant(false, strictMessage); + } +} +function getProjectionFunctionality(props) { + const { drag, layout } = featureDefinitions; + if (!drag && !layout) + return {}; + const combined = { ...drag, ...layout }; + return { + MeasureLayout: drag?.isEnabled(props) || layout?.isEnabled(props) + ? combined.MeasureLayout + : undefined, + ProjectionNode: combined.ProjectionNode, + }; +} + +const createHtmlRenderState = () => ({ + style: {}, + transform: {}, + transformOrigin: {}, + vars: {}, +}); + +function copyRawValuesOnly(target, source, props) { + for (const key in source) { + if (!motionDom.isMotionValue(source[key]) && !isForcedMotionValue(key, props)) { + target[key] = source[key]; + } + } +} +function useInitialMotionValues({ transformTemplate }, visualState) { + return React.useMemo(() => { + const state = createHtmlRenderState(); + buildHTMLStyles(state, visualState, transformTemplate); + return Object.assign({}, state.vars, state.style); + }, [visualState]); +} +function useStyle(props, visualState) { + const styleProp = props.style || {}; + const style = {}; + /** + * Copy non-Motion Values straight into style + */ + copyRawValuesOnly(style, styleProp, props); + Object.assign(style, useInitialMotionValues(props, visualState)); + return style; +} +function useHTMLProps(props, visualState) { + // The `any` isn't ideal but it is the type of createElement props argument + const htmlProps = {}; + const style = useStyle(props, visualState); + if (props.drag && props.dragListener !== false) { + // Disable the ghost element when a user drags + htmlProps.draggable = false; + // Disable text selection + style.userSelect = + style.WebkitUserSelect = + style.WebkitTouchCallout = + "none"; + // Disable scrolling on the draggable direction + style.touchAction = + props.drag === true + ? "none" + : `pan-${props.drag === "x" ? "y" : "x"}`; + } + if (props.tabIndex === undefined && + (props.onTap || props.onTapStart || props.whileTap)) { + htmlProps.tabIndex = 0; + } + htmlProps.style = style; + return htmlProps; +} + +const dashKeys = { + offset: "stroke-dashoffset", + array: "stroke-dasharray", +}; +const camelKeys = { + offset: "strokeDashoffset", + array: "strokeDasharray", +}; +/** + * Build SVG path properties. Uses the path's measured length to convert + * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset + * and stroke-dasharray attributes. + * + * This function is mutative to reduce per-frame GC. + */ +function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) { + // Normalise path length by setting SVG attribute pathLength to 1 + attrs.pathLength = 1; + // We use dash case when setting attributes directly to the DOM node and camel case + // when defining props on a React component. + const keys = useDashCase ? dashKeys : camelKeys; + // Build the dash offset + attrs[keys.offset] = motionDom.px.transform(-offset); + // Build the dash array + const pathLength = motionDom.px.transform(length); + const pathSpacing = motionDom.px.transform(spacing); + attrs[keys.array] = `${pathLength} ${pathSpacing}`; +} + +/** + * Build SVG visual attributes, like cx and style.transform + */ +function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0, +// This is object creation, which we try to avoid per-frame. +...latest }, isSVGTag, transformTemplate, styleProp) { + buildHTMLStyles(state, latest, transformTemplate); + /** + * For svg tags we just want to make sure viewBox is animatable and treat all the styles + * as normal HTML tags. + */ + if (isSVGTag) { + if (state.style.viewBox) { + state.attrs.viewBox = state.style.viewBox; + } + return; + } + state.attrs = state.style; + state.style = {}; + const { attrs, style } = state; + /** + * However, we apply transforms as CSS transforms. + * So if we detect a transform, transformOrigin we take it from attrs and copy it into style. + */ + if (attrs.transform) { + style.transform = attrs.transform; + delete attrs.transform; + } + if (style.transform || attrs.transformOrigin) { + style.transformOrigin = attrs.transformOrigin ?? "50% 50%"; + delete attrs.transformOrigin; + } + if (style.transform) { + /** + * SVG's element transform-origin uses its own median as a reference. + * Therefore, transformBox becomes a fill-box + */ + style.transformBox = styleProp?.transformBox ?? "fill-box"; + delete attrs.transformBox; + } + // Render attrX/attrY/attrScale as attributes + if (attrX !== undefined) + attrs.x = attrX; + if (attrY !== undefined) + attrs.y = attrY; + if (attrScale !== undefined) + attrs.scale = attrScale; + // Build SVG path if one has been defined + if (pathLength !== undefined) { + buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false); + } +} + +const createSvgRenderState = () => ({ + ...createHtmlRenderState(), + attrs: {}, +}); + +const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg"; + +function useSVGProps(props, visualState, _isStatic, Component) { + const visualProps = React.useMemo(() => { + const state = createSvgRenderState(); + buildSVGAttrs(state, visualState, isSVGTag(Component), props.transformTemplate, props.style); + return { + ...state.attrs, + style: { ...state.style }, + }; + }, [visualState]); + if (props.style) { + const rawStyles = {}; + copyRawValuesOnly(rawStyles, props.style, props); + visualProps.style = { ...rawStyles, ...visualProps.style }; + } + return visualProps; +} + +/** + * We keep these listed separately as we use the lowercase tag names as part + * of the runtime bundle to detect SVG components + */ +const lowercaseSVGElements = [ + "animate", + "circle", + "defs", + "desc", + "ellipse", + "g", + "image", + "line", + "filter", + "marker", + "mask", + "metadata", + "path", + "pattern", + "polygon", + "polyline", + "rect", + "stop", + "switch", + "symbol", + "svg", + "text", + "tspan", + "use", + "view", +]; + +function isSVGComponent(Component) { + if ( + /** + * If it's not a string, it's a custom React component. Currently we only support + * HTML custom React components. + */ + typeof Component !== "string" || + /** + * If it contains a dash, the element is a custom HTML webcomponent. + */ + Component.includes("-")) { + return false; + } + else if ( + /** + * If it's in our list of lowercase SVG tags, it's an SVG component + */ + lowercaseSVGElements.indexOf(Component) > -1 || + /** + * If it contains a capital letter, it's an SVG component + */ + /[A-Z]/u.test(Component)) { + return true; + } + return false; +} + +function createUseRender(forwardMotionProps = false) { + const useRender = (Component, props, ref, { latestValues }, isStatic) => { + const useVisualProps = isSVGComponent(Component) + ? useSVGProps + : useHTMLProps; + const visualProps = useVisualProps(props, latestValues, isStatic, Component); + const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps); + const elementProps = Component !== React.Fragment + ? { ...filteredProps, ...visualProps, ref } + : {}; + /** + * If component has been handed a motion value as its child, + * memoise its initial value and render that. Subsequent updates + * will be handled by the onChange handler + */ + const { children } = props; + const renderedChildren = React.useMemo(() => (motionDom.isMotionValue(children) ? children.get() : children), [children]); + return React.createElement(Component, { + ...elementProps, + children: renderedChildren, + }); + }; + return useRender; +} + +function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) { + const state = { + latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps), + renderState: createRenderState(), + }; + return state; +} +const makeUseVisualState = (config) => (props, isStatic) => { + const context = React.useContext(MotionContext); + const presenceContext = React.useContext(PresenceContext); + const make = () => makeState(config, props, context, presenceContext); + return isStatic ? make() : useConstant(make); +}; +function makeLatestValues(props, context, presenceContext, scrapeMotionValues) { + const values = {}; + const motionValues = scrapeMotionValues(props, {}); + for (const key in motionValues) { + values[key] = resolveMotionValue(motionValues[key]); + } + let { initial, animate } = props; + const isControllingVariants$1 = isControllingVariants(props); + const isVariantNode$1 = isVariantNode(props); + if (context && + isVariantNode$1 && + !isControllingVariants$1 && + props.inherit !== false) { + if (initial === undefined) + initial = context.initial; + if (animate === undefined) + animate = context.animate; + } + let isInitialAnimationBlocked = presenceContext + ? presenceContext.initial === false + : false; + isInitialAnimationBlocked = isInitialAnimationBlocked || initial === false; + const variantToSet = isInitialAnimationBlocked ? animate : initial; + if (variantToSet && + typeof variantToSet !== "boolean" && + !isAnimationControls(variantToSet)) { + const list = Array.isArray(variantToSet) ? variantToSet : [variantToSet]; + for (let i = 0; i < list.length; i++) { + const resolved = resolveVariantFromProps(props, list[i]); + if (resolved) { + const { transitionEnd, transition, ...target } = resolved; + for (const key in target) { + let valueTarget = target[key]; + if (Array.isArray(valueTarget)) { + /** + * Take final keyframe if the initial animation is blocked because + * we want to initialise at the end of that blocked animation. + */ + const index = isInitialAnimationBlocked + ? valueTarget.length - 1 + : 0; + valueTarget = valueTarget[index]; + } + if (valueTarget !== null) { + values[key] = valueTarget; + } + } + for (const key in transitionEnd) { + values[key] = transitionEnd[key]; + } + } + } + } + return values; +} + +const htmlMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps$1, + createRenderState: createHtmlRenderState, + }), +}; + +function scrapeMotionValuesFromProps(props, prevProps, visualElement) { + const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + for (const key in props) { + if (motionDom.isMotionValue(props[key]) || + motionDom.isMotionValue(prevProps[key])) { + const targetKey = motionDom.transformPropOrder.indexOf(key) !== -1 + ? "attr" + key.charAt(0).toUpperCase() + key.substring(1) + : key; + newValues[targetKey] = props[key]; + } + } + return newValues; +} + +const svgMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps, + createRenderState: createSvgRenderState, + }), +}; + +function createMotionComponentFactory(preloadedFeatures, createVisualElement) { + return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) { + const baseConfig = isSVGComponent(Component) + ? svgMotionConfig + : htmlMotionConfig; + const config = { + ...baseConfig, + preloadedFeatures, + useRender: createUseRender(forwardMotionProps), + createVisualElement, + Component, + }; + return createRendererMotionComponent(config); + }; +} + +/** + * A set of attribute names that are always read/written as camel case. + */ +const camelCaseAttributes = new Set([ + "baseFrequency", + "diffuseConstant", + "kernelMatrix", + "kernelUnitLength", + "keySplines", + "keyTimes", + "limitingConeAngle", + "markerHeight", + "markerWidth", + "numOctaves", + "targetX", + "targetY", + "surfaceScale", + "specularConstant", + "specularExponent", + "stdDeviation", + "tableValues", + "viewBox", + "gradientTransform", + "pathLength", + "startOffset", + "textLength", + "lengthAdjust", +]); + +function renderSVG(element, renderState, _styleProp, projection) { + renderHTML(element, renderState, undefined, projection); + for (const key in renderState.attrs) { + element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]); + } +} + +class SVGVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "svg"; + this.isSVGTag = false; + this.measureInstanceViewportBox = createBox; + } + getBaseTargetFromProps(props, key) { + return props[key]; + } + readValueFromInstance(instance, key) { + if (motionDom.transformProps.has(key)) { + const defaultType = motionDom.getDefaultValueType(key); + return defaultType ? defaultType.default || 0 : 0; + } + key = !camelCaseAttributes.has(key) ? camelToDash(key) : key; + return instance.getAttribute(key); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps(props, prevProps, visualElement); + } + build(renderState, latestValues, props) { + buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate, props.style); + } + renderInstance(instance, renderState, styleProp, projection) { + renderSVG(instance, renderState, styleProp, projection); + } + mount(instance) { + this.isSVGTag = isSVGTag(instance.tagName); + super.mount(instance); + } +} + +const createDomVisualElement = (Component, options) => { + return isSVGComponent(Component) + ? new SVGVisualElement(options) + : new HTMLVisualElement(options, { + allowProjection: Component !== React.Fragment, + }); +}; + +const createMotionComponent = /*@__PURE__*/ createMotionComponentFactory({ + ...animations, + ...gestureAnimations, + ...drag, + ...layout, +}, createDomVisualElement); + +exports.FlatTree = FlatTree; +exports.HTMLVisualElement = HTMLVisualElement; +exports.LayoutGroupContext = LayoutGroupContext; +exports.LazyContext = LazyContext; +exports.MotionConfigContext = MotionConfigContext; +exports.MotionContext = MotionContext; +exports.PresenceContext = PresenceContext; +exports.SVGVisualElement = SVGVisualElement; +exports.SwitchLayoutGroupContext = SwitchLayoutGroupContext; +exports.VisualElement = VisualElement; +exports.addDomEvent = addDomEvent; +exports.addPointerEvent = addPointerEvent; +exports.addPointerInfo = addPointerInfo; +exports.addScaleCorrector = addScaleCorrector; +exports.animateSingleValue = animateSingleValue; +exports.animateTarget = animateTarget; +exports.animateVisualElement = animateVisualElement; +exports.animations = animations; +exports.buildTransform = buildTransform; +exports.calcLength = calcLength; +exports.createBox = createBox; +exports.createDomVisualElement = createDomVisualElement; +exports.createMotionComponent = createMotionComponent; +exports.createMotionComponentFactory = createMotionComponentFactory; +exports.createRendererMotionComponent = createRendererMotionComponent; +exports.delay = delay; +exports.distance = distance; +exports.distance2D = distance2D; +exports.drag = drag; +exports.filterProps = filterProps; +exports.gestureAnimations = gestureAnimations; +exports.getOptimisedAppearId = getOptimisedAppearId; +exports.hasReducedMotionListener = hasReducedMotionListener; +exports.initPrefersReducedMotion = initPrefersReducedMotion; +exports.isBrowser = isBrowser; +exports.isValidMotionProp = isValidMotionProp; +exports.layout = layout; +exports.loadExternalIsValidProp = loadExternalIsValidProp; +exports.loadFeatures = loadFeatures; +exports.makeUseVisualState = makeUseVisualState; +exports.motionComponentSymbol = motionComponentSymbol; +exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute; +exports.optimizedAppearDataId = optimizedAppearDataId; +exports.prefersReducedMotion = prefersReducedMotion; +exports.resolveMotionValue = resolveMotionValue; +exports.rootProjectionNode = rootProjectionNode; +exports.setTarget = setTarget; +exports.useConstant = useConstant; +exports.useIsPresent = useIsPresent; +exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect; +exports.usePresence = usePresence; +exports.visualElementStore = visualElementStore; diff --git a/node_modules/framer-motion/dist/cjs/debug.js b/node_modules/framer-motion/dist/cjs/debug.js new file mode 100644 index 00000000..b8eba96b --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/debug.js @@ -0,0 +1,12 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var motionDom = require('motion-dom'); + + + +Object.defineProperty(exports, "recordStats", { + enumerable: true, + get: function () { return motionDom.recordStats; } +}); diff --git a/node_modules/framer-motion/dist/cjs/dom-mini.js b/node_modules/framer-motion/dist/cjs/dom-mini.js new file mode 100644 index 00000000..0bb1c6da --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/dom-mini.js @@ -0,0 +1,461 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var motionDom = require('motion-dom'); +var motionUtils = require('motion-utils'); + +function isDOMKeyframes(keyframes) { + return typeof keyframes === "object" && !Array.isArray(keyframes); +} + +function resolveSubjects(subject, keyframes, scope, selectorCache) { + if (typeof subject === "string" && isDOMKeyframes(keyframes)) { + return motionDom.resolveElements(subject, scope, selectorCache); + } + else if (subject instanceof NodeList) { + return Array.from(subject); + } + else if (Array.isArray(subject)) { + return subject; + } + else { + return [subject]; + } +} + +function calculateRepeatDuration(duration, repeat, _repeatDelay) { + return duration * (repeat + 1); +} + +/** + * Given a absolute or relative time definition and current/prev time state of the sequence, + * calculate an absolute time for the next keyframes. + */ +function calcNextTime(current, next, prev, labels) { + if (typeof next === "number") { + return next; + } + else if (next.startsWith("-") || next.startsWith("+")) { + return Math.max(0, current + parseFloat(next)); + } + else if (next === "<") { + return prev; + } + else if (next.startsWith("<")) { + return Math.max(0, prev + parseFloat(next.slice(1))); + } + else { + return labels.get(next) ?? current; + } +} + +function eraseKeyframes(sequence, startTime, endTime) { + for (let i = 0; i < sequence.length; i++) { + const keyframe = sequence[i]; + if (keyframe.at > startTime && keyframe.at < endTime) { + motionUtils.removeItem(sequence, keyframe); + // If we remove this item we have to push the pointer back one + i--; + } + } +} +function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) { + /** + * Erase every existing value between currentTime and targetTime, + * this will essentially splice this timeline into any currently + * defined ones. + */ + eraseKeyframes(sequence, startTime, endTime); + for (let i = 0; i < keyframes.length; i++) { + sequence.push({ + value: keyframes[i], + at: motionDom.mixNumber(startTime, endTime, offset[i]), + easing: motionUtils.getEasingForSegment(easing, i), + }); + } +} + +/** + * Take an array of times that represent repeated keyframes. For instance + * if we have original times of [0, 0.5, 1] then our repeated times will + * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back + * down to a 0-1 scale. + */ +function normalizeTimes(times, repeat) { + for (let i = 0; i < times.length; i++) { + times[i] = times[i] / (repeat + 1); + } +} + +function compareByTime(a, b) { + if (a.at === b.at) { + if (a.value === null) + return 1; + if (b.value === null) + return -1; + return 0; + } + else { + return a.at - b.at; + } +} + +const defaultSegmentEasing = "easeInOut"; +const MAX_REPEAT = 20; +function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) { + const defaultDuration = defaultTransition.duration || 0.3; + const animationDefinitions = new Map(); + const sequences = new Map(); + const elementCache = {}; + const timeLabels = new Map(); + let prevTime = 0; + let currentTime = 0; + let totalDuration = 0; + /** + * Build the timeline by mapping over the sequence array and converting + * the definitions into keyframes and offsets with absolute time values. + * These will later get converted into relative offsets in a second pass. + */ + for (let i = 0; i < sequence.length; i++) { + const segment = sequence[i]; + /** + * If this is a timeline label, mark it and skip the rest of this iteration. + */ + if (typeof segment === "string") { + timeLabels.set(segment, currentTime); + continue; + } + else if (!Array.isArray(segment)) { + timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels)); + continue; + } + let [subject, keyframes, transition = {}] = segment; + /** + * If a relative or absolute time value has been specified we need to resolve + * it in relation to the currentTime. + */ + if (transition.at !== undefined) { + currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels); + } + /** + * Keep track of the maximum duration in this definition. This will be + * applied to currentTime once the definition has been parsed. + */ + let maxDuration = 0; + const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => { + const valueKeyframesAsList = keyframesAsList(valueKeyframes); + const { delay = 0, times = motionDom.defaultOffset(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition; + let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition; + /** + * Resolve stagger() if defined. + */ + const calculatedDelay = typeof delay === "function" + ? delay(elementIndex, numSubjects) + : delay; + /** + * If this animation should and can use a spring, generate a spring easing function. + */ + const numKeyframes = valueKeyframesAsList.length; + const createGenerator = motionDom.isGenerator(type) + ? type + : generators?.[type || "keyframes"]; + if (numKeyframes <= 2 && createGenerator) { + /** + * As we're creating an easing function from a spring, + * ideally we want to generate it using the real distance + * between the two keyframes. However this isn't always + * possible - in these situations we use 0-100. + */ + let absoluteDelta = 100; + if (numKeyframes === 2 && + isNumberKeyframesArray(valueKeyframesAsList)) { + const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0]; + absoluteDelta = Math.abs(delta); + } + const springTransition = { ...remainingTransition }; + if (duration !== undefined) { + springTransition.duration = motionUtils.secondsToMilliseconds(duration); + } + const springEasing = motionDom.createGeneratorEasing(springTransition, absoluteDelta, createGenerator); + ease = springEasing.ease; + duration = springEasing.duration; + } + duration ?? (duration = defaultDuration); + const startTime = currentTime + calculatedDelay; + /** + * If there's only one time offset of 0, fill in a second with length 1 + */ + if (times.length === 1 && times[0] === 0) { + times[1] = 1; + } + /** + * Fill out if offset if fewer offsets than keyframes + */ + const remainder = times.length - valueKeyframesAsList.length; + remainder > 0 && motionDom.fillOffset(times, remainder); + /** + * If only one value has been set, ie [1], push a null to the start of + * the keyframe array. This will let us mark a keyframe at this point + * that will later be hydrated with the previous value. + */ + valueKeyframesAsList.length === 1 && + valueKeyframesAsList.unshift(null); + /** + * Handle repeat options + */ + if (repeat) { + motionUtils.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20"); + duration = calculateRepeatDuration(duration, repeat); + const originalKeyframes = [...valueKeyframesAsList]; + const originalTimes = [...times]; + ease = Array.isArray(ease) ? [...ease] : [ease]; + const originalEase = [...ease]; + for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { + valueKeyframesAsList.push(...originalKeyframes); + for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) { + times.push(originalTimes[keyframeIndex] + (repeatIndex + 1)); + ease.push(keyframeIndex === 0 + ? "linear" + : motionUtils.getEasingForSegment(originalEase, keyframeIndex - 1)); + } + } + normalizeTimes(times, repeat); + } + const targetTime = startTime + duration; + /** + * Add keyframes, mapping offsets to absolute time. + */ + addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime); + maxDuration = Math.max(calculatedDelay + duration, maxDuration); + totalDuration = Math.max(targetTime, totalDuration); + }; + if (motionDom.isMotionValue(subject)) { + const subjectSequence = getSubjectSequence(subject, sequences); + resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope, elementCache); + const numSubjects = subjects.length; + /** + * For every element in this segment, process the defined values. + */ + for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) { + /** + * Cast necessary, but we know these are of this type + */ + keyframes = keyframes; + transition = transition; + const thisSubject = subjects[subjectIndex]; + const subjectSequence = getSubjectSequence(thisSubject, sequences); + for (const key in keyframes) { + resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects); + } + } + } + prevTime = currentTime; + currentTime += maxDuration; + } + /** + * For every element and value combination create a new animation. + */ + sequences.forEach((valueSequences, element) => { + for (const key in valueSequences) { + const valueSequence = valueSequences[key]; + /** + * Arrange all the keyframes in ascending time order. + */ + valueSequence.sort(compareByTime); + const keyframes = []; + const valueOffset = []; + const valueEasing = []; + /** + * For each keyframe, translate absolute times into + * relative offsets based on the total duration of the timeline. + */ + for (let i = 0; i < valueSequence.length; i++) { + const { at, value, easing } = valueSequence[i]; + keyframes.push(value); + valueOffset.push(motionUtils.progress(0, totalDuration, at)); + valueEasing.push(easing || "easeOut"); + } + /** + * If the first keyframe doesn't land on offset: 0 + * provide one by duplicating the initial keyframe. This ensures + * it snaps to the first keyframe when the animation starts. + */ + if (valueOffset[0] !== 0) { + valueOffset.unshift(0); + keyframes.unshift(keyframes[0]); + valueEasing.unshift(defaultSegmentEasing); + } + /** + * If the last keyframe doesn't land on offset: 1 + * provide one with a null wildcard value. This will ensure it + * stays static until the end of the animation. + */ + if (valueOffset[valueOffset.length - 1] !== 1) { + valueOffset.push(1); + keyframes.push(null); + } + if (!animationDefinitions.has(element)) { + animationDefinitions.set(element, { + keyframes: {}, + transition: {}, + }); + } + const definition = animationDefinitions.get(element); + definition.keyframes[key] = keyframes; + definition.transition[key] = { + ...defaultTransition, + duration: totalDuration, + ease: valueEasing, + times: valueOffset, + ...sequenceTransition, + }; + } + }); + return animationDefinitions; +} +function getSubjectSequence(subject, sequences) { + !sequences.has(subject) && sequences.set(subject, {}); + return sequences.get(subject); +} +function getValueSequence(name, sequences) { + if (!sequences[name]) + sequences[name] = []; + return sequences[name]; +} +function keyframesAsList(keyframes) { + return Array.isArray(keyframes) ? keyframes : [keyframes]; +} +function getValueTransition(transition, key) { + return transition && transition[key] + ? { + ...transition, + ...transition[key], + } + : { ...transition }; +} +const isNumber = (keyframe) => typeof keyframe === "number"; +const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber); + +function animateElements(elementOrSelector, keyframes, options, scope) { + const elements = motionDom.resolveElements(elementOrSelector, scope); + const numElements = elements.length; + motionUtils.invariant(Boolean(numElements), "No valid element provided."); + /** + * WAAPI doesn't support interrupting animations. + * + * Therefore, starting animations requires a three-step process: + * 1. Stop existing animations (write styles to DOM) + * 2. Resolve keyframes (read styles from DOM) + * 3. Create new animations (write styles to DOM) + * + * The hybrid `animate()` function uses AsyncAnimation to resolve + * keyframes before creating new animations, which removes style + * thrashing. Here, we have much stricter filesize constraints. + * Therefore we do this in a synchronous way that ensures that + * at least within `animate()` calls there is no style thrashing. + * + * In the motion-native-animate-mini-interrupt benchmark this + * was 80% faster than a single loop. + */ + const animationDefinitions = []; + /** + * Step 1: Build options and stop existing animations (write) + */ + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + const elementTransition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if (typeof elementTransition.delay === "function") { + elementTransition.delay = elementTransition.delay(i, numElements); + } + for (const valueName in keyframes) { + let valueKeyframes = keyframes[valueName]; + if (!Array.isArray(valueKeyframes)) { + valueKeyframes = [valueKeyframes]; + } + const valueOptions = { + ...motionDom.getValueTransition(elementTransition, valueName), + }; + valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay)); + /** + * If there's an existing animation playing on this element then stop it + * before creating a new one. + */ + const map = motionDom.getAnimationMap(element); + const key = motionDom.animationMapKey(valueName, valueOptions.pseudoElement || ""); + const currentAnimation = map.get(key); + currentAnimation && currentAnimation.stop(); + animationDefinitions.push({ + map, + key, + unresolvedKeyframes: valueKeyframes, + options: { + ...valueOptions, + element, + name: valueName, + allowFlatten: !elementTransition.type && !elementTransition.ease, + }, + }); + } + } + /** + * Step 2: Resolve keyframes (read) + */ + for (let i = 0; i < animationDefinitions.length; i++) { + const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i]; + const { element, name, pseudoElement } = animationOptions; + if (!pseudoElement && unresolvedKeyframes[0] === null) { + unresolvedKeyframes[0] = motionDom.getComputedStyle(element, name); + } + motionDom.fillWildcards(unresolvedKeyframes); + motionDom.applyPxDefaults(unresolvedKeyframes, name); + /** + * If we only have one keyframe, explicitly read the initial keyframe + * from the computed style. This is to ensure consistency with WAAPI behaviour + * for restarting animations, for instance .play() after finish, when it + * has one vs two keyframes. + */ + if (!pseudoElement && unresolvedKeyframes.length < 2) { + unresolvedKeyframes.unshift(motionDom.getComputedStyle(element, name)); + } + animationOptions.keyframes = unresolvedKeyframes; + } + /** + * Step 3: Create new animations (write) + */ + const animations = []; + for (let i = 0; i < animationDefinitions.length; i++) { + const { map, key, options: animationOptions } = animationDefinitions[i]; + const animation = new motionDom.NativeAnimation(animationOptions); + map.set(key, animation); + animation.finished.finally(() => map.delete(key)); + animations.push(animation); + } + return animations; +} + +function animateSequence(definition, options) { + const animations = []; + createAnimationsFromSequence(definition, options).forEach(({ keyframes, transition }, element) => { + animations.push(...animateElements(element, keyframes, transition)); + }); + return new motionDom.GroupAnimationWithThen(animations); +} + +const createScopedWaapiAnimate = (scope) => { + function scopedAnimate(elementOrSelector, keyframes, options) { + return new motionDom.GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope)); + } + return scopedAnimate; +}; +const animateMini = /*@__PURE__*/ createScopedWaapiAnimate(); + +exports.animate = animateMini; +exports.animateSequence = animateSequence; diff --git a/node_modules/framer-motion/dist/cjs/dom.js b/node_modules/framer-motion/dist/cjs/dom.js new file mode 100644 index 00000000..be7c0738 --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/dom.js @@ -0,0 +1,2471 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var motionDom = require('motion-dom'); +var motionUtils = require('motion-utils'); + +function isDOMKeyframes(keyframes) { + return typeof keyframes === "object" && !Array.isArray(keyframes); +} + +function resolveSubjects(subject, keyframes, scope, selectorCache) { + if (typeof subject === "string" && isDOMKeyframes(keyframes)) { + return motionDom.resolveElements(subject, scope, selectorCache); + } + else if (subject instanceof NodeList) { + return Array.from(subject); + } + else if (Array.isArray(subject)) { + return subject; + } + else { + return [subject]; + } +} + +function calculateRepeatDuration(duration, repeat, _repeatDelay) { + return duration * (repeat + 1); +} + +/** + * Given a absolute or relative time definition and current/prev time state of the sequence, + * calculate an absolute time for the next keyframes. + */ +function calcNextTime(current, next, prev, labels) { + if (typeof next === "number") { + return next; + } + else if (next.startsWith("-") || next.startsWith("+")) { + return Math.max(0, current + parseFloat(next)); + } + else if (next === "<") { + return prev; + } + else if (next.startsWith("<")) { + return Math.max(0, prev + parseFloat(next.slice(1))); + } + else { + return labels.get(next) ?? current; + } +} + +function eraseKeyframes(sequence, startTime, endTime) { + for (let i = 0; i < sequence.length; i++) { + const keyframe = sequence[i]; + if (keyframe.at > startTime && keyframe.at < endTime) { + motionUtils.removeItem(sequence, keyframe); + // If we remove this item we have to push the pointer back one + i--; + } + } +} +function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) { + /** + * Erase every existing value between currentTime and targetTime, + * this will essentially splice this timeline into any currently + * defined ones. + */ + eraseKeyframes(sequence, startTime, endTime); + for (let i = 0; i < keyframes.length; i++) { + sequence.push({ + value: keyframes[i], + at: motionDom.mixNumber(startTime, endTime, offset[i]), + easing: motionUtils.getEasingForSegment(easing, i), + }); + } +} + +/** + * Take an array of times that represent repeated keyframes. For instance + * if we have original times of [0, 0.5, 1] then our repeated times will + * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back + * down to a 0-1 scale. + */ +function normalizeTimes(times, repeat) { + for (let i = 0; i < times.length; i++) { + times[i] = times[i] / (repeat + 1); + } +} + +function compareByTime(a, b) { + if (a.at === b.at) { + if (a.value === null) + return 1; + if (b.value === null) + return -1; + return 0; + } + else { + return a.at - b.at; + } +} + +const defaultSegmentEasing = "easeInOut"; +const MAX_REPEAT = 20; +function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) { + const defaultDuration = defaultTransition.duration || 0.3; + const animationDefinitions = new Map(); + const sequences = new Map(); + const elementCache = {}; + const timeLabels = new Map(); + let prevTime = 0; + let currentTime = 0; + let totalDuration = 0; + /** + * Build the timeline by mapping over the sequence array and converting + * the definitions into keyframes and offsets with absolute time values. + * These will later get converted into relative offsets in a second pass. + */ + for (let i = 0; i < sequence.length; i++) { + const segment = sequence[i]; + /** + * If this is a timeline label, mark it and skip the rest of this iteration. + */ + if (typeof segment === "string") { + timeLabels.set(segment, currentTime); + continue; + } + else if (!Array.isArray(segment)) { + timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels)); + continue; + } + let [subject, keyframes, transition = {}] = segment; + /** + * If a relative or absolute time value has been specified we need to resolve + * it in relation to the currentTime. + */ + if (transition.at !== undefined) { + currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels); + } + /** + * Keep track of the maximum duration in this definition. This will be + * applied to currentTime once the definition has been parsed. + */ + let maxDuration = 0; + const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => { + const valueKeyframesAsList = keyframesAsList(valueKeyframes); + const { delay = 0, times = motionDom.defaultOffset(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition; + let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition; + /** + * Resolve stagger() if defined. + */ + const calculatedDelay = typeof delay === "function" + ? delay(elementIndex, numSubjects) + : delay; + /** + * If this animation should and can use a spring, generate a spring easing function. + */ + const numKeyframes = valueKeyframesAsList.length; + const createGenerator = motionDom.isGenerator(type) + ? type + : generators?.[type || "keyframes"]; + if (numKeyframes <= 2 && createGenerator) { + /** + * As we're creating an easing function from a spring, + * ideally we want to generate it using the real distance + * between the two keyframes. However this isn't always + * possible - in these situations we use 0-100. + */ + let absoluteDelta = 100; + if (numKeyframes === 2 && + isNumberKeyframesArray(valueKeyframesAsList)) { + const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0]; + absoluteDelta = Math.abs(delta); + } + const springTransition = { ...remainingTransition }; + if (duration !== undefined) { + springTransition.duration = motionUtils.secondsToMilliseconds(duration); + } + const springEasing = motionDom.createGeneratorEasing(springTransition, absoluteDelta, createGenerator); + ease = springEasing.ease; + duration = springEasing.duration; + } + duration ?? (duration = defaultDuration); + const startTime = currentTime + calculatedDelay; + /** + * If there's only one time offset of 0, fill in a second with length 1 + */ + if (times.length === 1 && times[0] === 0) { + times[1] = 1; + } + /** + * Fill out if offset if fewer offsets than keyframes + */ + const remainder = times.length - valueKeyframesAsList.length; + remainder > 0 && motionDom.fillOffset(times, remainder); + /** + * If only one value has been set, ie [1], push a null to the start of + * the keyframe array. This will let us mark a keyframe at this point + * that will later be hydrated with the previous value. + */ + valueKeyframesAsList.length === 1 && + valueKeyframesAsList.unshift(null); + /** + * Handle repeat options + */ + if (repeat) { + motionUtils.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20"); + duration = calculateRepeatDuration(duration, repeat); + const originalKeyframes = [...valueKeyframesAsList]; + const originalTimes = [...times]; + ease = Array.isArray(ease) ? [...ease] : [ease]; + const originalEase = [...ease]; + for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { + valueKeyframesAsList.push(...originalKeyframes); + for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) { + times.push(originalTimes[keyframeIndex] + (repeatIndex + 1)); + ease.push(keyframeIndex === 0 + ? "linear" + : motionUtils.getEasingForSegment(originalEase, keyframeIndex - 1)); + } + } + normalizeTimes(times, repeat); + } + const targetTime = startTime + duration; + /** + * Add keyframes, mapping offsets to absolute time. + */ + addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime); + maxDuration = Math.max(calculatedDelay + duration, maxDuration); + totalDuration = Math.max(targetTime, totalDuration); + }; + if (motionDom.isMotionValue(subject)) { + const subjectSequence = getSubjectSequence(subject, sequences); + resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope, elementCache); + const numSubjects = subjects.length; + /** + * For every element in this segment, process the defined values. + */ + for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) { + /** + * Cast necessary, but we know these are of this type + */ + keyframes = keyframes; + transition = transition; + const thisSubject = subjects[subjectIndex]; + const subjectSequence = getSubjectSequence(thisSubject, sequences); + for (const key in keyframes) { + resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects); + } + } + } + prevTime = currentTime; + currentTime += maxDuration; + } + /** + * For every element and value combination create a new animation. + */ + sequences.forEach((valueSequences, element) => { + for (const key in valueSequences) { + const valueSequence = valueSequences[key]; + /** + * Arrange all the keyframes in ascending time order. + */ + valueSequence.sort(compareByTime); + const keyframes = []; + const valueOffset = []; + const valueEasing = []; + /** + * For each keyframe, translate absolute times into + * relative offsets based on the total duration of the timeline. + */ + for (let i = 0; i < valueSequence.length; i++) { + const { at, value, easing } = valueSequence[i]; + keyframes.push(value); + valueOffset.push(motionUtils.progress(0, totalDuration, at)); + valueEasing.push(easing || "easeOut"); + } + /** + * If the first keyframe doesn't land on offset: 0 + * provide one by duplicating the initial keyframe. This ensures + * it snaps to the first keyframe when the animation starts. + */ + if (valueOffset[0] !== 0) { + valueOffset.unshift(0); + keyframes.unshift(keyframes[0]); + valueEasing.unshift(defaultSegmentEasing); + } + /** + * If the last keyframe doesn't land on offset: 1 + * provide one with a null wildcard value. This will ensure it + * stays static until the end of the animation. + */ + if (valueOffset[valueOffset.length - 1] !== 1) { + valueOffset.push(1); + keyframes.push(null); + } + if (!animationDefinitions.has(element)) { + animationDefinitions.set(element, { + keyframes: {}, + transition: {}, + }); + } + const definition = animationDefinitions.get(element); + definition.keyframes[key] = keyframes; + definition.transition[key] = { + ...defaultTransition, + duration: totalDuration, + ease: valueEasing, + times: valueOffset, + ...sequenceTransition, + }; + } + }); + return animationDefinitions; +} +function getSubjectSequence(subject, sequences) { + !sequences.has(subject) && sequences.set(subject, {}); + return sequences.get(subject); +} +function getValueSequence(name, sequences) { + if (!sequences[name]) + sequences[name] = []; + return sequences[name]; +} +function keyframesAsList(keyframes) { + return Array.isArray(keyframes) ? keyframes : [keyframes]; +} +function getValueTransition(transition, key) { + return transition && transition[key] + ? { + ...transition, + ...transition[key], + } + : { ...transition }; +} +const isNumber = (keyframe) => typeof keyframe === "number"; +const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber); + +const visualElementStore = new WeakMap(); + +const isKeyframesTarget = (v) => { + return Array.isArray(v); +}; + +function getValueState(visualElement) { + const state = [{}, {}]; + visualElement?.values.forEach((value, key) => { + state[0][key] = value.get(); + state[1][key] = value.getVelocity(); + }); + return state; +} +function resolveVariantFromProps(props, definition, custom, visualElement) { + /** + * If the variant definition is a function, resolve. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + /** + * If the variant definition is a variant label, or + * the function returned a variant label, resolve. + */ + if (typeof definition === "string") { + definition = props.variants && props.variants[definition]; + } + /** + * At this point we've resolved both functions and variant labels, + * but the resolved variant label might itself have been a function. + * If so, resolve. This can only have returned a valid target object. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + return definition; +} + +function resolveVariant(visualElement, definition, custom) { + const props = visualElement.getProps(); + return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement); +} + +/** + * Set VisualElement's MotionValue, creating a new MotionValue for it if + * it doesn't exist. + */ +function setMotionValue(visualElement, key, value) { + if (visualElement.hasValue(key)) { + visualElement.getValue(key).set(value); + } + else { + visualElement.addValue(key, motionDom.motionValue(value)); + } +} +function resolveFinalValueInKeyframes(v) { + // TODO maybe throw if v.length - 1 is placeholder token? + return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; +} +function setTarget(visualElement, definition) { + const resolved = resolveVariant(visualElement, definition); + let { transitionEnd = {}, transition = {}, ...target } = resolved || {}; + target = { ...target, ...transitionEnd }; + for (const key in target) { + const value = resolveFinalValueInKeyframes(target[key]); + setMotionValue(visualElement, key, value); + } +} + +function isWillChangeMotionValue(value) { + return Boolean(motionDom.isMotionValue(value) && value.add); +} + +function addValueToWillChange(visualElement, key) { + const willChange = visualElement.getValue("willChange"); + /** + * It could be that a user has set willChange to a regular MotionValue, + * in which case we can't add the value to it. + */ + if (isWillChangeMotionValue(willChange)) { + return willChange.add(key); + } + else if (!willChange && motionUtils.MotionGlobalConfig.WillChange) { + const newWillChange = new motionUtils.MotionGlobalConfig.WillChange("auto"); + visualElement.addValue("willChange", newWillChange); + newWillChange.add(key); + } +} + +/** + * Convert camelCase to dash-case properties. + */ +const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase(); + +const optimizedAppearDataId = "framerAppearId"; +const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId); + +function getOptimisedAppearId(visualElement) { + return visualElement.props[optimizedAppearDataAttribute]; +} + +const isNotNull = (value) => value !== null; +function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const index = repeat && repeatType !== "loop" && repeat % 2 === 1 + ? 0 + : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; +} + +const underDampedSpring = { + type: "spring", + stiffness: 500, + damping: 25, + restSpeed: 10, +}; +const criticallyDampedSpring = (target) => ({ + type: "spring", + stiffness: 550, + damping: target === 0 ? 2 * Math.sqrt(550) : 30, + restSpeed: 10, +}); +const keyframesTransition = { + type: "keyframes", + duration: 0.8, +}; +/** + * Default easing curve is a slightly shallower version of + * the default browser easing curve. + */ +const ease = { + type: "keyframes", + ease: [0.25, 0.1, 0.35, 1], + duration: 0.3, +}; +const getDefaultTransition = (valueKey, { keyframes }) => { + if (keyframes.length > 2) { + return keyframesTransition; + } + else if (motionDom.transformProps.has(valueKey)) { + return valueKey.startsWith("scale") + ? criticallyDampedSpring(keyframes[1]) + : underDampedSpring; + } + return ease; +}; + +/** + * Decide whether a transition is defined on a given Transition. + * This filters out orchestration options and returns true + * if any options are left. + */ +function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) { + return !!Object.keys(transition).length; +} + +const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => { + const valueTransition = motionDom.getValueTransition(transition, name) || {}; + /** + * Most transition values are currently completely overwritten by value-specific + * transitions. In the future it'd be nicer to blend these transitions. But for now + * delay actually does inherit from the root transition if not value-specific. + */ + const delay = valueTransition.delay || transition.delay || 0; + /** + * Elapsed isn't a public transition option but can be passed through from + * optimized appear effects in milliseconds. + */ + let { elapsed = 0 } = transition; + elapsed = elapsed - motionUtils.secondsToMilliseconds(delay); + const options = { + keyframes: Array.isArray(target) ? target : [null, target], + ease: "easeOut", + velocity: value.getVelocity(), + ...valueTransition, + delay: -elapsed, + onUpdate: (v) => { + value.set(v); + valueTransition.onUpdate && valueTransition.onUpdate(v); + }, + onComplete: () => { + onComplete(); + valueTransition.onComplete && valueTransition.onComplete(); + }, + name, + motionValue: value, + element: isHandoff ? undefined : element, + }; + /** + * If there's no transition defined for this value, we can generate + * unique transition settings for this value. + */ + if (!isTransitionDefined(valueTransition)) { + Object.assign(options, getDefaultTransition(name, options)); + } + /** + * Both WAAPI and our internal animation functions use durations + * as defined by milliseconds, while our external API defines them + * as seconds. + */ + options.duration && (options.duration = motionUtils.secondsToMilliseconds(options.duration)); + options.repeatDelay && (options.repeatDelay = motionUtils.secondsToMilliseconds(options.repeatDelay)); + /** + * Support deprecated way to set initial value. Prefer keyframe syntax. + */ + if (options.from !== undefined) { + options.keyframes[0] = options.from; + } + let shouldSkip = false; + if (options.type === false || + (options.duration === 0 && !options.repeatDelay)) { + options.duration = 0; + if (options.delay === 0) { + shouldSkip = true; + } + } + if (motionUtils.MotionGlobalConfig.instantAnimations || + motionUtils.MotionGlobalConfig.skipAnimations) { + shouldSkip = true; + options.duration = 0; + options.delay = 0; + } + /** + * If the transition type or easing has been explicitly set by the user + * then we don't want to allow flattening the animation. + */ + options.allowFlatten = !valueTransition.type && !valueTransition.ease; + /** + * If we can or must skip creating the animation, and apply only + * the final keyframe, do so. We also check once keyframes are resolved but + * this early check prevents the need to create an animation at all. + */ + if (shouldSkip && !isHandoff && value.get() !== undefined) { + const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition); + if (finalKeyframe !== undefined) { + motionDom.frame.update(() => { + options.onUpdate(finalKeyframe); + options.onComplete(); + }); + return; + } + } + return valueTransition.isSync + ? new motionDom.JSAnimation(options) + : new motionDom.AsyncMotionValueAnimation(options); +}; + +/** + * Decide whether we should block this animation. Previously, we achieved this + * just by checking whether the key was listed in protectedKeys, but this + * posed problems if an animation was triggered by afterChildren and protectedKeys + * had been set to true in the meantime. + */ +function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) { + const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true; + needsAnimating[key] = false; + return shouldBlock; +} +function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) { + let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition; + if (transitionOverride) + transition = transitionOverride; + const animations = []; + const animationTypeState = type && + visualElement.animationState && + visualElement.animationState.getState()[type]; + for (const key in target) { + const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null); + const valueTarget = target[key]; + if (valueTarget === undefined || + (animationTypeState && + shouldBlockAnimation(animationTypeState, key))) { + continue; + } + const valueTransition = { + delay, + ...motionDom.getValueTransition(transition || {}, key), + }; + /** + * If the value is already at the defined target, skip the animation. + */ + const currentValue = value.get(); + if (currentValue !== undefined && + !value.isAnimating && + !Array.isArray(valueTarget) && + valueTarget === currentValue && + !valueTransition.velocity) { + continue; + } + /** + * If this is the first time a value is being animated, check + * to see if we're handling off from an existing animation. + */ + let isHandoff = false; + if (window.MotionHandoffAnimation) { + const appearId = getOptimisedAppearId(visualElement); + if (appearId) { + const startTime = window.MotionHandoffAnimation(appearId, key, motionDom.frame); + if (startTime !== null) { + valueTransition.startTime = startTime; + isHandoff = true; + } + } + } + addValueToWillChange(visualElement, key); + value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && motionDom.positionalKeys.has(key) + ? { type: false } + : valueTransition, visualElement, isHandoff)); + const animation = value.animation; + if (animation) { + animations.push(animation); + } + } + if (transitionEnd) { + Promise.all(animations).then(() => { + motionDom.frame.update(() => { + transitionEnd && setTarget(visualElement, transitionEnd); + }); + }); + } + return animations; +} + +/** + * Bounding boxes tend to be defined as top, left, right, bottom. For various operations + * it's easier to consider each axis individually. This function returns a bounding box + * as a map of single-axis min/max values. + */ +function convertBoundingBoxToBox({ top, left, right, bottom, }) { + return { + x: { min: left, max: right }, + y: { min: top, max: bottom }, + }; +} +/** + * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function + * provided by Framer to allow measured points to be corrected for device scaling. This is used + * when measuring DOM elements and DOM event points. + */ +function transformBoxPoints(point, transformPoint) { + if (!transformPoint) + return point; + const topLeft = transformPoint({ x: point.left, y: point.top }); + const bottomRight = transformPoint({ x: point.right, y: point.bottom }); + return { + top: topLeft.y, + left: topLeft.x, + bottom: bottomRight.y, + right: bottomRight.x, + }; +} + +function measureViewportBox(instance, transformPoint) { + return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint)); +} + +const featureProps = { + animation: [ + "animate", + "variants", + "whileHover", + "whileTap", + "exit", + "whileInView", + "whileFocus", + "whileDrag", + ], + exit: ["exit"], + drag: ["drag", "dragControls"], + focus: ["whileFocus"], + hover: ["whileHover", "onHoverStart", "onHoverEnd"], + tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"], + pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"], + inView: ["whileInView", "onViewportEnter", "onViewportLeave"], + layout: ["layout", "layoutId"], +}; +const featureDefinitions = {}; +for (const key in featureProps) { + featureDefinitions[key] = { + isEnabled: (props) => featureProps[key].some((name) => !!props[name]), + }; +} + +const createAxis = () => ({ min: 0, max: 0 }); +const createBox = () => ({ + x: createAxis(), + y: createAxis(), +}); + +const isBrowser = typeof window !== "undefined"; + +// Does this device prefer reduced motion? Returns `null` server-side. +const prefersReducedMotion = { current: null }; +const hasReducedMotionListener = { current: false }; + +function initPrefersReducedMotion() { + hasReducedMotionListener.current = true; + if (!isBrowser) + return; + if (window.matchMedia) { + const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)"); + const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches); + motionMediaQuery.addEventListener("change", setReducedMotionPreferences); + setReducedMotionPreferences(); + } + else { + prefersReducedMotion.current = false; + } +} + +function isAnimationControls(v) { + return (v !== null && + typeof v === "object" && + typeof v.start === "function"); +} + +/** + * Decides if the supplied variable is variant label + */ +function isVariantLabel(v) { + return typeof v === "string" || Array.isArray(v); +} + +const variantPriorityOrder = [ + "animate", + "whileInView", + "whileFocus", + "whileHover", + "whileTap", + "whileDrag", + "exit", +]; +const variantProps = ["initial", ...variantPriorityOrder]; + +function isControllingVariants(props) { + return (isAnimationControls(props.animate) || + variantProps.some((name) => isVariantLabel(props[name]))); +} +function isVariantNode(props) { + return Boolean(isControllingVariants(props) || props.variants); +} + +function updateMotionValuesFromProps(element, next, prev) { + for (const key in next) { + const nextValue = next[key]; + const prevValue = prev[key]; + if (motionDom.isMotionValue(nextValue)) { + /** + * If this is a motion value found in props or style, we want to add it + * to our visual element's motion value map. + */ + element.addValue(key, nextValue); + } + else if (motionDom.isMotionValue(prevValue)) { + /** + * If we're swapping from a motion value to a static value, + * create a new motion value from that + */ + element.addValue(key, motionDom.motionValue(nextValue, { owner: element })); + } + else if (prevValue !== nextValue) { + /** + * If this is a flat value that has changed, update the motion value + * or create one if it doesn't exist. We only want to do this if we're + * not handling the value with our animation state. + */ + if (element.hasValue(key)) { + const existingValue = element.getValue(key); + if (existingValue.liveStyle === true) { + existingValue.jump(nextValue); + } + else if (!existingValue.hasAnimated) { + existingValue.set(nextValue); + } + } + else { + const latestValue = element.getStaticValue(key); + element.addValue(key, motionDom.motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element })); + } + } + } + // Handle removed values + for (const key in prev) { + if (next[key] === undefined) + element.removeValue(key); + } + return next; +} + +const propEventHandlers = [ + "AnimationStart", + "AnimationComplete", + "Update", + "BeforeLayoutMeasure", + "LayoutMeasure", + "LayoutAnimationStart", + "LayoutAnimationComplete", +]; +/** + * A VisualElement is an imperative abstraction around UI elements such as + * HTMLElement, SVGElement, Three.Object3D etc. + */ +class VisualElement { + /** + * This method takes React props and returns found MotionValues. For example, HTML + * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays. + * + * This isn't an abstract method as it needs calling in the constructor, but it is + * intended to be one. + */ + scrapeMotionValuesFromProps(_props, _prevProps, _visualElement) { + return {}; + } + constructor({ parent, props, presenceContext, reducedMotionConfig, blockInitialAnimation, visualState, }, options = {}) { + /** + * A reference to the current underlying Instance, e.g. a HTMLElement + * or Three.Mesh etc. + */ + this.current = null; + /** + * A set containing references to this VisualElement's children. + */ + this.children = new Set(); + /** + * Determine what role this visual element should take in the variant tree. + */ + this.isVariantNode = false; + this.isControllingVariants = false; + /** + * Decides whether this VisualElement should animate in reduced motion + * mode. + * + * TODO: This is currently set on every individual VisualElement but feels + * like it could be set globally. + */ + this.shouldReduceMotion = null; + /** + * A map of all motion values attached to this visual element. Motion + * values are source of truth for any given animated value. A motion + * value might be provided externally by the component via props. + */ + this.values = new Map(); + this.KeyframeResolver = motionDom.KeyframeResolver; + /** + * Cleanup functions for active features (hover/tap/exit etc) + */ + this.features = {}; + /** + * A map of every subscription that binds the provided or generated + * motion values onChange listeners to this visual element. + */ + this.valueSubscriptions = new Map(); + /** + * A reference to the previously-provided motion values as returned + * from scrapeMotionValuesFromProps. We use the keys in here to determine + * if any motion values need to be removed after props are updated. + */ + this.prevMotionValues = {}; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + /** + * An object containing an unsubscribe function for each prop event subscription. + * For example, every "Update" event can have multiple subscribers via + * VisualElement.on(), but only one of those can be defined via the onUpdate prop. + */ + this.propEventSubscriptions = {}; + this.notifyUpdate = () => this.notify("Update", this.latestValues); + this.render = () => { + if (!this.current) + return; + this.triggerBuild(); + this.renderInstance(this.current, this.renderState, this.props.style, this.projection); + }; + this.renderScheduledAt = 0.0; + this.scheduleRender = () => { + const now = motionDom.time.now(); + if (this.renderScheduledAt < now) { + this.renderScheduledAt = now; + motionDom.frame.render(this.render, false, true); + } + }; + const { latestValues, renderState } = visualState; + this.latestValues = latestValues; + this.baseTarget = { ...latestValues }; + this.initialValues = props.initial ? { ...latestValues } : {}; + this.renderState = renderState; + this.parent = parent; + this.props = props; + this.presenceContext = presenceContext; + this.depth = parent ? parent.depth + 1 : 0; + this.reducedMotionConfig = reducedMotionConfig; + this.options = options; + this.blockInitialAnimation = Boolean(blockInitialAnimation); + this.isControllingVariants = isControllingVariants(props); + this.isVariantNode = isVariantNode(props); + if (this.isVariantNode) { + this.variantChildren = new Set(); + } + this.manuallyAnimateOnMount = Boolean(parent && parent.current); + /** + * Any motion values that are provided to the element when created + * aren't yet bound to the element, as this would technically be impure. + * However, we iterate through the motion values and set them to the + * initial values for this component. + * + * TODO: This is impure and we should look at changing this to run on mount. + * Doing so will break some tests but this isn't necessarily a breaking change, + * more a reflection of the test. + */ + const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {}, this); + for (const key in initialMotionValues) { + const value = initialMotionValues[key]; + if (latestValues[key] !== undefined && motionDom.isMotionValue(value)) { + value.set(latestValues[key], false); + } + } + } + mount(instance) { + this.current = instance; + visualElementStore.set(instance, this); + if (this.projection && !this.projection.instance) { + this.projection.mount(instance); + } + if (this.parent && this.isVariantNode && !this.isControllingVariants) { + this.removeFromVariantTree = this.parent.addVariantChild(this); + } + this.values.forEach((value, key) => this.bindToMotionValue(key, value)); + if (!hasReducedMotionListener.current) { + initPrefersReducedMotion(); + } + this.shouldReduceMotion = + this.reducedMotionConfig === "never" + ? false + : this.reducedMotionConfig === "always" + ? true + : prefersReducedMotion.current; + if (process.env.NODE_ENV !== "production") { + motionUtils.warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + if (this.parent) + this.parent.children.add(this); + this.update(this.props, this.presenceContext); + } + unmount() { + this.projection && this.projection.unmount(); + motionDom.cancelFrame(this.notifyUpdate); + motionDom.cancelFrame(this.render); + this.valueSubscriptions.forEach((remove) => remove()); + this.valueSubscriptions.clear(); + this.removeFromVariantTree && this.removeFromVariantTree(); + this.parent && this.parent.children.delete(this); + for (const key in this.events) { + this.events[key].clear(); + } + for (const key in this.features) { + const feature = this.features[key]; + if (feature) { + feature.unmount(); + feature.isMounted = false; + } + } + this.current = null; + } + bindToMotionValue(key, value) { + if (this.valueSubscriptions.has(key)) { + this.valueSubscriptions.get(key)(); + } + const valueIsTransform = motionDom.transformProps.has(key); + if (valueIsTransform && this.onBindTransform) { + this.onBindTransform(); + } + const removeOnChange = value.on("change", (latestValue) => { + this.latestValues[key] = latestValue; + this.props.onUpdate && motionDom.frame.preRender(this.notifyUpdate); + if (valueIsTransform && this.projection) { + this.projection.isTransformDirty = true; + } + }); + const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender); + let removeSyncCheck; + if (window.MotionCheckAppearSync) { + removeSyncCheck = window.MotionCheckAppearSync(this, key, value); + } + this.valueSubscriptions.set(key, () => { + removeOnChange(); + removeOnRenderRequest(); + if (removeSyncCheck) + removeSyncCheck(); + if (value.owner) + value.stop(); + }); + } + sortNodePosition(other) { + /** + * If these nodes aren't even of the same type we can't compare their depth. + */ + if (!this.current || + !this.sortInstanceNodePosition || + this.type !== other.type) { + return 0; + } + return this.sortInstanceNodePosition(this.current, other.current); + } + updateFeatures() { + let key = "animation"; + for (key in featureDefinitions) { + const featureDefinition = featureDefinitions[key]; + if (!featureDefinition) + continue; + const { isEnabled, Feature: FeatureConstructor } = featureDefinition; + /** + * If this feature is enabled but not active, make a new instance. + */ + if (!this.features[key] && + FeatureConstructor && + isEnabled(this.props)) { + this.features[key] = new FeatureConstructor(this); + } + /** + * If we have a feature, mount or update it. + */ + if (this.features[key]) { + const feature = this.features[key]; + if (feature.isMounted) { + feature.update(); + } + else { + feature.mount(); + feature.isMounted = true; + } + } + } + } + triggerBuild() { + this.build(this.renderState, this.latestValues, this.props); + } + /** + * Measure the current viewport box with or without transforms. + * Only measures axis-aligned boxes, rotate and skew must be manually + * removed with a re-render to work. + */ + measureViewportBox() { + return this.current + ? this.measureInstanceViewportBox(this.current, this.props) + : createBox(); + } + getStaticValue(key) { + return this.latestValues[key]; + } + setStaticValue(key, value) { + this.latestValues[key] = value; + } + /** + * Update the provided props. Ensure any newly-added motion values are + * added to our map, old ones removed, and listeners updated. + */ + update(props, presenceContext) { + if (props.transformTemplate || this.props.transformTemplate) { + this.scheduleRender(); + } + this.prevProps = this.props; + this.props = props; + this.prevPresenceContext = this.presenceContext; + this.presenceContext = presenceContext; + /** + * Update prop event handlers ie onAnimationStart, onAnimationComplete + */ + for (let i = 0; i < propEventHandlers.length; i++) { + const key = propEventHandlers[i]; + if (this.propEventSubscriptions[key]) { + this.propEventSubscriptions[key](); + delete this.propEventSubscriptions[key]; + } + const listenerName = ("on" + key); + const listener = props[listenerName]; + if (listener) { + this.propEventSubscriptions[key] = this.on(key, listener); + } + } + this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps, this), this.prevMotionValues); + if (this.handleChildMotionValue) { + this.handleChildMotionValue(); + } + } + getProps() { + return this.props; + } + /** + * Returns the variant definition with a given name. + */ + getVariant(name) { + return this.props.variants ? this.props.variants[name] : undefined; + } + /** + * Returns the defined default transition on this component. + */ + getDefaultTransition() { + return this.props.transition; + } + getTransformPagePoint() { + return this.props.transformPagePoint; + } + getClosestVariantNode() { + return this.isVariantNode + ? this + : this.parent + ? this.parent.getClosestVariantNode() + : undefined; + } + /** + * Add a child visual element to our set of children. + */ + addVariantChild(child) { + const closestVariantNode = this.getClosestVariantNode(); + if (closestVariantNode) { + closestVariantNode.variantChildren && + closestVariantNode.variantChildren.add(child); + return () => closestVariantNode.variantChildren.delete(child); + } + } + /** + * Add a motion value and bind it to this visual element. + */ + addValue(key, value) { + // Remove existing value if it exists + const existingValue = this.values.get(key); + if (value !== existingValue) { + if (existingValue) + this.removeValue(key); + this.bindToMotionValue(key, value); + this.values.set(key, value); + this.latestValues[key] = value.get(); + } + } + /** + * Remove a motion value and unbind any active subscriptions. + */ + removeValue(key) { + this.values.delete(key); + const unsubscribe = this.valueSubscriptions.get(key); + if (unsubscribe) { + unsubscribe(); + this.valueSubscriptions.delete(key); + } + delete this.latestValues[key]; + this.removeValueFromRenderState(key, this.renderState); + } + /** + * Check whether we have a motion value for this key + */ + hasValue(key) { + return this.values.has(key); + } + getValue(key, defaultValue) { + if (this.props.values && this.props.values[key]) { + return this.props.values[key]; + } + let value = this.values.get(key); + if (value === undefined && defaultValue !== undefined) { + value = motionDom.motionValue(defaultValue === null ? undefined : defaultValue, { owner: this }); + this.addValue(key, value); + } + return value; + } + /** + * If we're trying to animate to a previously unencountered value, + * we need to check for it in our state and as a last resort read it + * directly from the instance (which might have performance implications). + */ + readValue(key, target) { + let value = this.latestValues[key] !== undefined || !this.current + ? this.latestValues[key] + : this.getBaseTargetFromProps(this.props, key) ?? + this.readValueFromInstance(this.current, key, this.options); + if (value !== undefined && value !== null) { + if (typeof value === "string" && + (motionUtils.isNumericalString(value) || motionUtils.isZeroValueString(value))) { + // If this is a number read as a string, ie "0" or "200", convert it to a number + value = parseFloat(value); + } + else if (!motionDom.findValueType(value) && motionDom.complex.test(target)) { + value = motionDom.getAnimatableNone(key, target); + } + this.setBaseTarget(key, motionDom.isMotionValue(value) ? value.get() : value); + } + return motionDom.isMotionValue(value) ? value.get() : value; + } + /** + * Set the base target to later animate back to. This is currently + * only hydrated on creation and when we first read a value. + */ + setBaseTarget(key, value) { + this.baseTarget[key] = value; + } + /** + * Find the base target for a value thats been removed from all animation + * props. + */ + getBaseTarget(key) { + const { initial } = this.props; + let valueFromInitial; + if (typeof initial === "string" || typeof initial === "object") { + const variant = resolveVariantFromProps(this.props, initial, this.presenceContext?.custom); + if (variant) { + valueFromInitial = variant[key]; + } + } + /** + * If this value still exists in the current initial variant, read that. + */ + if (initial && valueFromInitial !== undefined) { + return valueFromInitial; + } + /** + * Alternatively, if this VisualElement config has defined a getBaseTarget + * so we can read the value from an alternative source, try that. + */ + const target = this.getBaseTargetFromProps(this.props, key); + if (target !== undefined && !motionDom.isMotionValue(target)) + return target; + /** + * If the value was initially defined on initial, but it doesn't any more, + * return undefined. Otherwise return the value as initially read from the DOM. + */ + return this.initialValues[key] !== undefined && + valueFromInitial === undefined + ? undefined + : this.baseTarget[key]; + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new motionUtils.SubscriptionManager(); + } + return this.events[eventName].add(callback); + } + notify(eventName, ...args) { + if (this.events[eventName]) { + this.events[eventName].notify(...args); + } + } +} + +class DOMVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.KeyframeResolver = motionDom.DOMKeyframesResolver; + } + sortInstanceNodePosition(a, b) { + /** + * compareDocumentPosition returns a bitmask, by using the bitwise & + * we're returning true if 2 in that bitmask is set to true. 2 is set + * to true if b preceeds a. + */ + return a.compareDocumentPosition(b) & 2 ? 1 : -1; + } + getBaseTargetFromProps(props, key) { + return props.style + ? props.style[key] + : undefined; + } + removeValueFromRenderState(key, { vars, style }) { + delete vars[key]; + delete style[key]; + } + handleChildMotionValue() { + if (this.childSubscription) { + this.childSubscription(); + delete this.childSubscription; + } + const { children } = this.props; + if (motionDom.isMotionValue(children)) { + this.childSubscription = children.on("change", (latest) => { + if (this.current) { + this.current.textContent = `${latest}`; + } + }); + } + } +} + +const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", +}; +const numTransforms = motionDom.transformPropOrder.length; +/** + * Build a CSS transform style from individual x/y/scale etc properties. + * + * This outputs with a default order of transforms/scales/rotations, this can be customised by + * providing a transformTemplate function. + */ +function buildTransform(latestValues, transform, transformTemplate) { + // The transform string we're going to build into. + let transformString = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < numTransforms; i++) { + const key = motionDom.transformPropOrder[i]; + const value = latestValues[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault || transformTemplate) { + const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]); + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + transformString += `${transformName}(${valueAsType}) `; + } + if (transformTemplate) { + transform[key] = valueAsType; + } + } + } + transformString = transformString.trim(); + // If we have a custom `transform` template, pass our transform values and + // generated transformString to that before returning + if (transformTemplate) { + transformString = transformTemplate(transform, transformIsDefault ? "" : transformString); + } + else if (transformIsDefault) { + transformString = "none"; + } + return transformString; +} + +function buildHTMLStyles(state, latestValues, transformTemplate) { + const { style, vars, transformOrigin } = state; + // Track whether we encounter any transform or transformOrigin values. + let hasTransform = false; + let hasTransformOrigin = false; + /** + * Loop over all our latest animated values and decide whether to handle them + * as a style or CSS variable. + * + * Transforms and transform origins are kept separately for further processing. + */ + for (const key in latestValues) { + const value = latestValues[key]; + if (motionDom.transformProps.has(key)) { + // If this is a transform, flag to enable further transform processing + hasTransform = true; + continue; + } + else if (motionDom.isCSSVariableName(key)) { + vars[key] = value; + continue; + } + else { + // Convert the value to its default value type, ie 0 -> "0px" + const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]); + if (key.startsWith("origin")) { + // If this is a transform origin, flag and enable further transform-origin processing + hasTransformOrigin = true; + transformOrigin[key] = + valueAsType; + } + else { + style[key] = valueAsType; + } + } + } + if (!latestValues.transform) { + if (hasTransform || transformTemplate) { + style.transform = buildTransform(latestValues, state.transform, transformTemplate); + } + else if (style.transform) { + /** + * If we have previously created a transform but currently don't have any, + * reset transform style to none. + */ + style.transform = "none"; + } + } + /** + * Build a transformOrigin style. Uses the same defaults as the browser for + * undefined origins. + */ + if (hasTransformOrigin) { + const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin; + style.transformOrigin = `${originX} ${originY} ${originZ}`; + } +} + +function renderHTML(element, { style, vars }, styleProp, projection) { + const elementStyle = element.style; + let key; + for (key in style) { + // CSSStyleDeclaration has [index: number]: string; in the types, so we use that as key type. + elementStyle[key] = style[key]; + } + // Write projection styles directly to element style + projection?.applyProjectionStyles(elementStyle, styleProp); + for (key in vars) { + // Loop over any CSS variables and assign those. + // They can only be assigned using `setProperty`. + elementStyle.setProperty(key, vars[key]); + } +} + +const scaleCorrectors = {}; + +function isForcedMotionValue(key, { layout, layoutId }) { + return (motionDom.transformProps.has(key) || + key.startsWith("origin") || + ((layout || layoutId !== undefined) && + (!!scaleCorrectors[key] || key === "opacity"))); +} + +function scrapeMotionValuesFromProps$1(props, prevProps, visualElement) { + const { style } = props; + const newValues = {}; + for (const key in style) { + if (motionDom.isMotionValue(style[key]) || + (prevProps.style && + motionDom.isMotionValue(prevProps.style[key])) || + isForcedMotionValue(key, props) || + visualElement?.getValue(key)?.liveStyle !== undefined) { + newValues[key] = style[key]; + } + } + return newValues; +} + +function getComputedStyle$1(element) { + return window.getComputedStyle(element); +} +class HTMLVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "html"; + this.renderInstance = renderHTML; + } + readValueFromInstance(instance, key) { + if (motionDom.transformProps.has(key)) { + return this.projection?.isProjecting + ? motionDom.defaultTransformValue(key) + : motionDom.readTransformValue(instance, key); + } + else { + const computedStyle = getComputedStyle$1(instance); + const value = (motionDom.isCSSVariableName(key) + ? computedStyle.getPropertyValue(key) + : computedStyle[key]) || 0; + return typeof value === "string" ? value.trim() : value; + } + } + measureInstanceViewportBox(instance, { transformPagePoint }) { + return measureViewportBox(instance, transformPagePoint); + } + build(renderState, latestValues, props) { + buildHTMLStyles(renderState, latestValues, props.transformTemplate); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + } +} + +function isObjectKey(key, object) { + return key in object; +} +class ObjectVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.type = "object"; + } + readValueFromInstance(instance, key) { + if (isObjectKey(key, instance)) { + const value = instance[key]; + if (typeof value === "string" || typeof value === "number") { + return value; + } + } + return undefined; + } + getBaseTargetFromProps() { + return undefined; + } + removeValueFromRenderState(key, renderState) { + delete renderState.output[key]; + } + measureInstanceViewportBox() { + return createBox(); + } + build(renderState, latestValues) { + Object.assign(renderState.output, latestValues); + } + renderInstance(instance, { output }) { + Object.assign(instance, output); + } + sortInstanceNodePosition() { + return 0; + } +} + +const dashKeys = { + offset: "stroke-dashoffset", + array: "stroke-dasharray", +}; +const camelKeys = { + offset: "strokeDashoffset", + array: "strokeDasharray", +}; +/** + * Build SVG path properties. Uses the path's measured length to convert + * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset + * and stroke-dasharray attributes. + * + * This function is mutative to reduce per-frame GC. + */ +function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) { + // Normalise path length by setting SVG attribute pathLength to 1 + attrs.pathLength = 1; + // We use dash case when setting attributes directly to the DOM node and camel case + // when defining props on a React component. + const keys = useDashCase ? dashKeys : camelKeys; + // Build the dash offset + attrs[keys.offset] = motionDom.px.transform(-offset); + // Build the dash array + const pathLength = motionDom.px.transform(length); + const pathSpacing = motionDom.px.transform(spacing); + attrs[keys.array] = `${pathLength} ${pathSpacing}`; +} + +/** + * Build SVG visual attributes, like cx and style.transform + */ +function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0, +// This is object creation, which we try to avoid per-frame. +...latest }, isSVGTag, transformTemplate, styleProp) { + buildHTMLStyles(state, latest, transformTemplate); + /** + * For svg tags we just want to make sure viewBox is animatable and treat all the styles + * as normal HTML tags. + */ + if (isSVGTag) { + if (state.style.viewBox) { + state.attrs.viewBox = state.style.viewBox; + } + return; + } + state.attrs = state.style; + state.style = {}; + const { attrs, style } = state; + /** + * However, we apply transforms as CSS transforms. + * So if we detect a transform, transformOrigin we take it from attrs and copy it into style. + */ + if (attrs.transform) { + style.transform = attrs.transform; + delete attrs.transform; + } + if (style.transform || attrs.transformOrigin) { + style.transformOrigin = attrs.transformOrigin ?? "50% 50%"; + delete attrs.transformOrigin; + } + if (style.transform) { + /** + * SVG's element transform-origin uses its own median as a reference. + * Therefore, transformBox becomes a fill-box + */ + style.transformBox = styleProp?.transformBox ?? "fill-box"; + delete attrs.transformBox; + } + // Render attrX/attrY/attrScale as attributes + if (attrX !== undefined) + attrs.x = attrX; + if (attrY !== undefined) + attrs.y = attrY; + if (attrScale !== undefined) + attrs.scale = attrScale; + // Build SVG path if one has been defined + if (pathLength !== undefined) { + buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false); + } +} + +/** + * A set of attribute names that are always read/written as camel case. + */ +const camelCaseAttributes = new Set([ + "baseFrequency", + "diffuseConstant", + "kernelMatrix", + "kernelUnitLength", + "keySplines", + "keyTimes", + "limitingConeAngle", + "markerHeight", + "markerWidth", + "numOctaves", + "targetX", + "targetY", + "surfaceScale", + "specularConstant", + "specularExponent", + "stdDeviation", + "tableValues", + "viewBox", + "gradientTransform", + "pathLength", + "startOffset", + "textLength", + "lengthAdjust", +]); + +const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg"; + +function renderSVG(element, renderState, _styleProp, projection) { + renderHTML(element, renderState, undefined, projection); + for (const key in renderState.attrs) { + element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]); + } +} + +function scrapeMotionValuesFromProps(props, prevProps, visualElement) { + const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + for (const key in props) { + if (motionDom.isMotionValue(props[key]) || + motionDom.isMotionValue(prevProps[key])) { + const targetKey = motionDom.transformPropOrder.indexOf(key) !== -1 + ? "attr" + key.charAt(0).toUpperCase() + key.substring(1) + : key; + newValues[targetKey] = props[key]; + } + } + return newValues; +} + +class SVGVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "svg"; + this.isSVGTag = false; + this.measureInstanceViewportBox = createBox; + } + getBaseTargetFromProps(props, key) { + return props[key]; + } + readValueFromInstance(instance, key) { + if (motionDom.transformProps.has(key)) { + const defaultType = motionDom.getDefaultValueType(key); + return defaultType ? defaultType.default || 0 : 0; + } + key = !camelCaseAttributes.has(key) ? camelToDash(key) : key; + return instance.getAttribute(key); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps(props, prevProps, visualElement); + } + build(renderState, latestValues, props) { + buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate, props.style); + } + renderInstance(instance, renderState, styleProp, projection) { + renderSVG(instance, renderState, styleProp, projection); + } + mount(instance) { + this.isSVGTag = isSVGTag(instance.tagName); + super.mount(instance); + } +} + +function createDOMVisualElement(element) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + transform: {}, + transformOrigin: {}, + style: {}, + vars: {}, + attrs: {}, + }, + latestValues: {}, + }, + }; + const node = motionDom.isSVGElement(element) && !motionDom.isSVGSVGElement(element) + ? new SVGVisualElement(options) + : new HTMLVisualElement(options); + node.mount(element); + visualElementStore.set(element, node); +} +function createObjectVisualElement(subject) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + output: {}, + }, + latestValues: {}, + }, + }; + const node = new ObjectVisualElement(options); + node.mount(subject); + visualElementStore.set(subject, node); +} + +function animateSingleValue(value, keyframes, options) { + const motionValue = motionDom.isMotionValue(value) ? value : motionDom.motionValue(value); + motionValue.start(animateMotionValue("", motionValue, keyframes, options)); + return motionValue.animation; +} + +function isSingleValue(subject, keyframes) { + return (motionDom.isMotionValue(subject) || + typeof subject === "number" || + (typeof subject === "string" && !isDOMKeyframes(keyframes))); +} +/** + * Implementation + */ +function animateSubject(subject, keyframes, options, scope) { + const animations = []; + if (isSingleValue(subject, keyframes)) { + animations.push(animateSingleValue(subject, isDOMKeyframes(keyframes) + ? keyframes.default || keyframes + : keyframes, options ? options.default || options : options)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope); + const numSubjects = subjects.length; + motionUtils.invariant(Boolean(numSubjects), "No valid elements provided."); + for (let i = 0; i < numSubjects; i++) { + const thisSubject = subjects[i]; + const createVisualElement = thisSubject instanceof Element + ? createDOMVisualElement + : createObjectVisualElement; + if (!visualElementStore.has(thisSubject)) { + createVisualElement(thisSubject); + } + const visualElement = visualElementStore.get(thisSubject); + const transition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if ("delay" in transition && + typeof transition.delay === "function") { + transition.delay = transition.delay(i, numSubjects); + } + animations.push(...animateTarget(visualElement, { ...keyframes, transition }, {})); + } + } + return animations; +} + +function animateSequence(sequence, options, scope) { + const animations = []; + const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring: motionDom.spring }); + animationDefinitions.forEach(({ keyframes, transition }, subject) => { + animations.push(...animateSubject(subject, keyframes, transition)); + }); + return animations; +} + +function isSequence(value) { + return Array.isArray(value) && value.some(Array.isArray); +} +/** + * Creates an animation function that is optionally scoped + * to a specific element. + */ +function createScopedAnimate(scope) { + /** + * Implementation + */ + function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) { + let animations = []; + if (isSequence(subjectOrSequence)) { + animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope); + } + else { + animations = animateSubject(subjectOrSequence, optionsOrKeyframes, options, scope); + } + const animation = new motionDom.GroupAnimationWithThen(animations); + if (scope) { + scope.animations.push(animation); + animation.finished.then(() => { + motionUtils.removeItem(scope.animations, animation); + }); + } + return animation; + } + return scopedAnimate; +} +const animate = createScopedAnimate(); + +function animateElements(elementOrSelector, keyframes, options, scope) { + const elements = motionDom.resolveElements(elementOrSelector, scope); + const numElements = elements.length; + motionUtils.invariant(Boolean(numElements), "No valid element provided."); + /** + * WAAPI doesn't support interrupting animations. + * + * Therefore, starting animations requires a three-step process: + * 1. Stop existing animations (write styles to DOM) + * 2. Resolve keyframes (read styles from DOM) + * 3. Create new animations (write styles to DOM) + * + * The hybrid `animate()` function uses AsyncAnimation to resolve + * keyframes before creating new animations, which removes style + * thrashing. Here, we have much stricter filesize constraints. + * Therefore we do this in a synchronous way that ensures that + * at least within `animate()` calls there is no style thrashing. + * + * In the motion-native-animate-mini-interrupt benchmark this + * was 80% faster than a single loop. + */ + const animationDefinitions = []; + /** + * Step 1: Build options and stop existing animations (write) + */ + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + const elementTransition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if (typeof elementTransition.delay === "function") { + elementTransition.delay = elementTransition.delay(i, numElements); + } + for (const valueName in keyframes) { + let valueKeyframes = keyframes[valueName]; + if (!Array.isArray(valueKeyframes)) { + valueKeyframes = [valueKeyframes]; + } + const valueOptions = { + ...motionDom.getValueTransition(elementTransition, valueName), + }; + valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay)); + /** + * If there's an existing animation playing on this element then stop it + * before creating a new one. + */ + const map = motionDom.getAnimationMap(element); + const key = motionDom.animationMapKey(valueName, valueOptions.pseudoElement || ""); + const currentAnimation = map.get(key); + currentAnimation && currentAnimation.stop(); + animationDefinitions.push({ + map, + key, + unresolvedKeyframes: valueKeyframes, + options: { + ...valueOptions, + element, + name: valueName, + allowFlatten: !elementTransition.type && !elementTransition.ease, + }, + }); + } + } + /** + * Step 2: Resolve keyframes (read) + */ + for (let i = 0; i < animationDefinitions.length; i++) { + const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i]; + const { element, name, pseudoElement } = animationOptions; + if (!pseudoElement && unresolvedKeyframes[0] === null) { + unresolvedKeyframes[0] = motionDom.getComputedStyle(element, name); + } + motionDom.fillWildcards(unresolvedKeyframes); + motionDom.applyPxDefaults(unresolvedKeyframes, name); + /** + * If we only have one keyframe, explicitly read the initial keyframe + * from the computed style. This is to ensure consistency with WAAPI behaviour + * for restarting animations, for instance .play() after finish, when it + * has one vs two keyframes. + */ + if (!pseudoElement && unresolvedKeyframes.length < 2) { + unresolvedKeyframes.unshift(motionDom.getComputedStyle(element, name)); + } + animationOptions.keyframes = unresolvedKeyframes; + } + /** + * Step 3: Create new animations (write) + */ + const animations = []; + for (let i = 0; i < animationDefinitions.length; i++) { + const { map, key, options: animationOptions } = animationDefinitions[i]; + const animation = new motionDom.NativeAnimation(animationOptions); + map.set(key, animation); + animation.finished.finally(() => map.delete(key)); + animations.push(animation); + } + return animations; +} + +const createScopedWaapiAnimate = (scope) => { + function scopedAnimate(elementOrSelector, keyframes, options) { + return new motionDom.GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope)); + } + return scopedAnimate; +}; +const animateMini = /*@__PURE__*/ createScopedWaapiAnimate(); + +/** + * A time in milliseconds, beyond which we consider the scroll velocity to be 0. + */ +const maxElapsed = 50; +const createAxisInfo = () => ({ + current: 0, + offset: [], + progress: 0, + scrollLength: 0, + targetOffset: 0, + targetLength: 0, + containerLength: 0, + velocity: 0, +}); +const createScrollInfo = () => ({ + time: 0, + x: createAxisInfo(), + y: createAxisInfo(), +}); +const keys = { + x: { + length: "Width", + position: "Left", + }, + y: { + length: "Height", + position: "Top", + }, +}; +function updateAxisInfo(element, axisName, info, time) { + const axis = info[axisName]; + const { length, position } = keys[axisName]; + const prev = axis.current; + const prevTime = info.time; + axis.current = element[`scroll${position}`]; + axis.scrollLength = element[`scroll${length}`] - element[`client${length}`]; + axis.offset.length = 0; + axis.offset[0] = 0; + axis.offset[1] = axis.scrollLength; + axis.progress = motionUtils.progress(0, axis.scrollLength, axis.current); + const elapsed = time - prevTime; + axis.velocity = + elapsed > maxElapsed + ? 0 + : motionUtils.velocityPerSecond(axis.current - prev, elapsed); +} +function updateScrollInfo(element, info, time) { + updateAxisInfo(element, "x", info, time); + updateAxisInfo(element, "y", info, time); + info.time = time; +} + +function calcInset(element, container) { + const inset = { x: 0, y: 0 }; + let current = element; + while (current && current !== container) { + if (motionDom.isHTMLElement(current)) { + inset.x += current.offsetLeft; + inset.y += current.offsetTop; + current = current.offsetParent; + } + else if (current.tagName === "svg") { + /** + * This isn't an ideal approach to measuring the offset of tags. + * It would be preferable, given they behave like HTMLElements in most ways + * to use offsetLeft/Top. But these don't exist on . Likewise we + * can't use .getBBox() like most SVG elements as these provide the offset + * relative to the SVG itself, which for is usually 0x0. + */ + const svgBoundingBox = current.getBoundingClientRect(); + current = current.parentElement; + const parentBoundingBox = current.getBoundingClientRect(); + inset.x += svgBoundingBox.left - parentBoundingBox.left; + inset.y += svgBoundingBox.top - parentBoundingBox.top; + } + else if (current instanceof SVGGraphicsElement) { + const { x, y } = current.getBBox(); + inset.x += x; + inset.y += y; + let svg = null; + let parent = current.parentNode; + while (!svg) { + if (parent.tagName === "svg") { + svg = parent; + } + parent = current.parentNode; + } + current = svg; + } + else { + break; + } + } + return inset; +} + +const namedEdges = { + start: 0, + center: 0.5, + end: 1, +}; +function resolveEdge(edge, length, inset = 0) { + let delta = 0; + /** + * If we have this edge defined as a preset, replace the definition + * with the numerical value. + */ + if (edge in namedEdges) { + edge = namedEdges[edge]; + } + /** + * Handle unit values + */ + if (typeof edge === "string") { + const asNumber = parseFloat(edge); + if (edge.endsWith("px")) { + delta = asNumber; + } + else if (edge.endsWith("%")) { + edge = asNumber / 100; + } + else if (edge.endsWith("vw")) { + delta = (asNumber / 100) * document.documentElement.clientWidth; + } + else if (edge.endsWith("vh")) { + delta = (asNumber / 100) * document.documentElement.clientHeight; + } + else { + edge = asNumber; + } + } + /** + * If the edge is defined as a number, handle as a progress value. + */ + if (typeof edge === "number") { + delta = length * edge; + } + return inset + delta; +} + +const defaultOffset = [0, 0]; +function resolveOffset(offset, containerLength, targetLength, targetInset) { + let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset; + let targetPoint = 0; + let containerPoint = 0; + if (typeof offset === "number") { + /** + * If we're provided offset: [0, 0.5, 1] then each number x should become + * [x, x], so we default to the behaviour of mapping 0 => 0 of both target + * and container etc. + */ + offsetDefinition = [offset, offset]; + } + else if (typeof offset === "string") { + offset = offset.trim(); + if (offset.includes(" ")) { + offsetDefinition = offset.split(" "); + } + else { + /** + * If we're provided a definition like "100px" then we want to apply + * that only to the top of the target point, leaving the container at 0. + * Whereas a named offset like "end" should be applied to both. + */ + offsetDefinition = [offset, namedEdges[offset] ? offset : `0`]; + } + } + targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset); + containerPoint = resolveEdge(offsetDefinition[1], containerLength); + return targetPoint - containerPoint; +} + +const ScrollOffset = { + Enter: [ + [0, 1], + [1, 1], + ], + Exit: [ + [0, 0], + [1, 0], + ], + Any: [ + [1, 0], + [0, 1], + ], + All: [ + [0, 0], + [1, 1], + ], +}; + +const point = { x: 0, y: 0 }; +function getTargetSize(target) { + return "getBBox" in target && target.tagName !== "svg" + ? target.getBBox() + : { width: target.clientWidth, height: target.clientHeight }; +} +function resolveOffsets(container, info, options) { + const { offset: offsetDefinition = ScrollOffset.All } = options; + const { target = container, axis = "y" } = options; + const lengthLabel = axis === "y" ? "height" : "width"; + const inset = target !== container ? calcInset(target, container) : point; + /** + * Measure the target and container. If they're the same thing then we + * use the container's scrollWidth/Height as the target, from there + * all other calculations can remain the same. + */ + const targetSize = target === container + ? { width: container.scrollWidth, height: container.scrollHeight } + : getTargetSize(target); + const containerSize = { + width: container.clientWidth, + height: container.clientHeight, + }; + /** + * Reset the length of the resolved offset array rather than creating a new one. + * TODO: More reusable data structures for targetSize/containerSize would also be good. + */ + info[axis].offset.length = 0; + /** + * Populate the offset array by resolving the user's offset definition into + * a list of pixel scroll offets. + */ + let hasChanged = !info[axis].interpolate; + const numOffsets = offsetDefinition.length; + for (let i = 0; i < numOffsets; i++) { + const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]); + if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) { + hasChanged = true; + } + info[axis].offset[i] = offset; + } + /** + * If the pixel scroll offsets have changed, create a new interpolator function + * to map scroll value into a progress. + */ + if (hasChanged) { + info[axis].interpolate = motionDom.interpolate(info[axis].offset, motionDom.defaultOffset(offsetDefinition), { clamp: false }); + info[axis].interpolatorOffsets = [...info[axis].offset]; + } + info[axis].progress = motionUtils.clamp(0, 1, info[axis].interpolate(info[axis].current)); +} + +function measure(container, target = container, info) { + /** + * Find inset of target within scrollable container + */ + info.x.targetOffset = 0; + info.y.targetOffset = 0; + if (target !== container) { + let node = target; + while (node && node !== container) { + info.x.targetOffset += node.offsetLeft; + info.y.targetOffset += node.offsetTop; + node = node.offsetParent; + } + } + info.x.targetLength = + target === container ? target.scrollWidth : target.clientWidth; + info.y.targetLength = + target === container ? target.scrollHeight : target.clientHeight; + info.x.containerLength = container.clientWidth; + info.y.containerLength = container.clientHeight; + /** + * In development mode ensure scroll containers aren't position: static as this makes + * it difficult to measure their relative positions. + */ + if (process.env.NODE_ENV !== "production") { + if (container && target && target !== container) { + motionUtils.warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly."); + } + } +} +function createOnScrollHandler(element, onScroll, info, options = {}) { + return { + measure: (time) => { + measure(element, options.target, info); + updateScrollInfo(element, info, time); + if (options.offset || options.target) { + resolveOffsets(element, info, options); + } + }, + notify: () => onScroll(info), + }; +} + +const scrollListeners = new WeakMap(); +const resizeListeners = new WeakMap(); +const onScrollHandlers = new WeakMap(); +const getEventTarget = (element) => element === document.scrollingElement ? window : element; +function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) { + if (!container) + return motionUtils.noop; + let containerHandlers = onScrollHandlers.get(container); + /** + * Get the onScroll handlers for this container. + * If one isn't found, create a new one. + */ + if (!containerHandlers) { + containerHandlers = new Set(); + onScrollHandlers.set(container, containerHandlers); + } + /** + * Create a new onScroll handler for the provided callback. + */ + const info = createScrollInfo(); + const containerHandler = createOnScrollHandler(container, onScroll, info, options); + containerHandlers.add(containerHandler); + /** + * Check if there's a scroll event listener for this container. + * If not, create one. + */ + if (!scrollListeners.has(container)) { + const measureAll = () => { + for (const handler of containerHandlers) { + handler.measure(motionDom.frameData.timestamp); + } + motionDom.frame.preUpdate(notifyAll); + }; + const notifyAll = () => { + for (const handler of containerHandlers) { + handler.notify(); + } + }; + const listener = () => motionDom.frame.read(measureAll); + scrollListeners.set(container, listener); + const target = getEventTarget(container); + window.addEventListener("resize", listener, { passive: true }); + if (container !== document.documentElement) { + resizeListeners.set(container, motionDom.resize(container, listener)); + } + target.addEventListener("scroll", listener, { passive: true }); + listener(); + } + const listener = scrollListeners.get(container); + motionDom.frame.read(listener, false, true); + return () => { + motionDom.cancelFrame(listener); + /** + * Check if we even have any handlers for this container. + */ + const currentHandlers = onScrollHandlers.get(container); + if (!currentHandlers) + return; + currentHandlers.delete(containerHandler); + if (currentHandlers.size) + return; + /** + * If no more handlers, remove the scroll listener too. + */ + const scrollListener = scrollListeners.get(container); + scrollListeners.delete(container); + if (scrollListener) { + getEventTarget(container).removeEventListener("scroll", scrollListener); + resizeListeners.get(container)?.(); + window.removeEventListener("resize", scrollListener); + } + }; +} + +const timelineCache = new Map(); +function scrollTimelineFallback(options) { + const currentTime = { value: 0 }; + const cancel = scrollInfo((info) => { + currentTime.value = info[options.axis].progress * 100; + }, options); + return { currentTime, cancel }; +} +function getTimeline({ source, container, ...options }) { + const { axis } = options; + if (source) + container = source; + const containerCache = timelineCache.get(container) ?? new Map(); + timelineCache.set(container, containerCache); + const targetKey = options.target ?? "self"; + const targetCache = containerCache.get(targetKey) ?? {}; + const axisKey = axis + (options.offset ?? []).join(","); + if (!targetCache[axisKey]) { + targetCache[axisKey] = + !options.target && motionDom.supportsScrollTimeline() + ? new ScrollTimeline({ source: container, axis }) + : scrollTimelineFallback({ container, ...options }); + } + return targetCache[axisKey]; +} + +function attachToAnimation(animation, options) { + const timeline = getTimeline(options); + return animation.attachTimeline({ + timeline: options.target ? undefined : timeline, + observe: (valueAnimation) => { + valueAnimation.pause(); + return motionDom.observeTimeline((progress) => { + valueAnimation.time = valueAnimation.duration * progress; + }, timeline); + }, + }); +} + +/** + * If the onScroll function has two arguments, it's expecting + * more specific information about the scroll from scrollInfo. + */ +function isOnScrollWithInfo(onScroll) { + return onScroll.length === 2; +} +function attachToFunction(onScroll, options) { + if (isOnScrollWithInfo(onScroll)) { + return scrollInfo((info) => { + onScroll(info[options.axis].progress, info); + }, options); + } + else { + return motionDom.observeTimeline(onScroll, getTimeline(options)); + } +} + +function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) { + if (!container) + return motionUtils.noop; + const optionsWithDefaults = { axis, container, ...options }; + return typeof onScroll === "function" + ? attachToFunction(onScroll, optionsWithDefaults) + : attachToAnimation(onScroll, optionsWithDefaults); +} + +const thresholds = { + some: 0, + all: 1, +}; +function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) { + const elements = motionDom.resolveElements(elementOrSelector); + const activeIntersections = new WeakMap(); + const onIntersectionChange = (entries) => { + entries.forEach((entry) => { + const onEnd = activeIntersections.get(entry.target); + /** + * If there's no change to the intersection, we don't need to + * do anything here. + */ + if (entry.isIntersecting === Boolean(onEnd)) + return; + if (entry.isIntersecting) { + const newOnEnd = onStart(entry.target, entry); + if (typeof newOnEnd === "function") { + activeIntersections.set(entry.target, newOnEnd); + } + else { + observer.unobserve(entry.target); + } + } + else if (typeof onEnd === "function") { + onEnd(entry); + activeIntersections.delete(entry.target); + } + }); + }; + const observer = new IntersectionObserver(onIntersectionChange, { + root, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholds[amount], + }); + elements.forEach((element) => observer.observe(element)); + return () => observer.disconnect(); +} + +/** + * Timeout defined in ms + */ +function delay(callback, timeout) { + const start = motionDom.time.now(); + const checkElapsed = ({ timestamp }) => { + const elapsed = timestamp - start; + if (elapsed >= timeout) { + motionDom.cancelFrame(checkElapsed); + callback(elapsed - timeout); + } + }; + motionDom.frame.setup(checkElapsed, true); + return () => motionDom.cancelFrame(checkElapsed); +} +function delayInSeconds(callback, timeout) { + return delay(callback, motionUtils.secondsToMilliseconds(timeout)); +} + +const distance = (a, b) => Math.abs(a - b); +function distance2D(a, b) { + // Multi-dimensional + const xDelta = distance(a.x, b.x); + const yDelta = distance(a.y, b.y); + return Math.sqrt(xDelta ** 2 + yDelta ** 2); +} + +exports.animate = animate; +exports.animateMini = animateMini; +exports.createScopedAnimate = createScopedAnimate; +exports.delay = delayInSeconds; +exports.distance = distance; +exports.distance2D = distance2D; +exports.inView = inView; +exports.scroll = scroll; +exports.scrollInfo = scrollInfo; +Object.keys(motionDom).forEach(function (k) { + if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { + enumerable: true, + get: function () { return motionDom[k]; } + }); +}); +Object.keys(motionUtils).forEach(function (k) { + if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { + enumerable: true, + get: function () { return motionUtils[k]; } + }); +}); diff --git a/node_modules/framer-motion/dist/cjs/index.js b/node_modules/framer-motion/dist/cjs/index.js new file mode 100644 index 00000000..deeef150 --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/index.js @@ -0,0 +1,2939 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var jsxRuntime = require('react/jsx-runtime'); +var React = require('react'); +var create = require('./create-C-c1JfhA.js'); +var motionDom = require('motion-dom'); +var motionUtils = require('motion-utils'); + +function _interopNamespaceDefault(e) { + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n.default = e; + return Object.freeze(n); +} + +var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); + +/** + * Measurement functionality has to be within a separate component + * to leverage snapshot lifecycle. + */ +class PopChildMeasure extends React__namespace.Component { + getSnapshotBeforeUpdate(prevProps) { + const element = this.props.childRef.current; + if (element && prevProps.isPresent && !this.props.isPresent) { + const parent = element.offsetParent; + const parentWidth = motionDom.isHTMLElement(parent) + ? parent.offsetWidth || 0 + : 0; + const size = this.props.sizeRef.current; + size.height = element.offsetHeight || 0; + size.width = element.offsetWidth || 0; + size.top = element.offsetTop; + size.left = element.offsetLeft; + size.right = parentWidth - size.width - size.left; + } + return null; + } + /** + * Required with getSnapshotBeforeUpdate to stop React complaining. + */ + componentDidUpdate() { } + render() { + return this.props.children; + } +} +function PopChild({ children, isPresent, anchorX, root }) { + const id = React.useId(); + const ref = React.useRef(null); + const size = React.useRef({ + width: 0, + height: 0, + top: 0, + left: 0, + right: 0, + }); + const { nonce } = React.useContext(create.MotionConfigContext); + /** + * We create and inject a style block so we can apply this explicit + * sizing in a non-destructive manner by just deleting the style block. + * + * We can't apply size via render as the measurement happens + * in getSnapshotBeforeUpdate (post-render), likewise if we apply the + * styles directly on the DOM node, we might be overwriting + * styles set via the style prop. + */ + React.useInsertionEffect(() => { + const { width, height, top, left, right } = size.current; + if (isPresent || !ref.current || !width || !height) + return; + const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`; + ref.current.dataset.motionPopId = id; + const style = document.createElement("style"); + if (nonce) + style.nonce = nonce; + const parent = root ?? document.head; + parent.appendChild(style); + if (style.sheet) { + style.sheet.insertRule(` + [data-motion-pop-id="${id}"] { + position: absolute !important; + width: ${width}px !important; + height: ${height}px !important; + ${x}px !important; + top: ${top}px !important; + } + `); + } + return () => { + parent.removeChild(style); + if (parent.contains(style)) { + parent.removeChild(style); + } + }; + }, [isPresent]); + return (jsxRuntime.jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) })); +} + +const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, root }) => { + const presenceChildren = create.useConstant(newChildrenMap); + const id = React.useId(); + let isReusedContext = true; + let context = React.useMemo(() => { + isReusedContext = false; + return { + id, + initial, + isPresent, + custom, + onExitComplete: (childId) => { + presenceChildren.set(childId, true); + for (const isComplete of presenceChildren.values()) { + if (!isComplete) + return; // can stop searching when any is incomplete + } + onExitComplete && onExitComplete(); + }, + register: (childId) => { + presenceChildren.set(childId, false); + return () => presenceChildren.delete(childId); + }, + }; + }, [isPresent, presenceChildren, onExitComplete]); + /** + * If the presence of a child affects the layout of the components around it, + * we want to make a new context value to ensure they get re-rendered + * so they can detect that layout change. + */ + if (presenceAffectsLayout && isReusedContext) { + context = { ...context }; + } + React.useMemo(() => { + presenceChildren.forEach((_, key) => presenceChildren.set(key, false)); + }, [isPresent]); + /** + * If there's no `motion` components to fire exit animations, we want to remove this + * component immediately. + */ + React__namespace.useEffect(() => { + !isPresent && + !presenceChildren.size && + onExitComplete && + onExitComplete(); + }, [isPresent]); + if (mode === "popLayout") { + children = (jsxRuntime.jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, root: root, children: children })); + } + return (jsxRuntime.jsx(create.PresenceContext.Provider, { value: context, children: children })); +}; +function newChildrenMap() { + return new Map(); +} + +const getChildKey = (child) => child.key || ""; +function onlyElements(children) { + const filtered = []; + // We use forEach here instead of map as map mutates the component key by preprending `.$` + React.Children.forEach(children, (child) => { + if (React.isValidElement(child)) + filtered.push(child); + }); + return filtered; +} + +/** + * `AnimatePresence` enables the animation of components that have been removed from the tree. + * + * When adding/removing more than a single child, every child **must** be given a unique `key` prop. + * + * Any `motion` components that have an `exit` property defined will animate out when removed from + * the tree. + * + * ```jsx + * import { motion, AnimatePresence } from 'framer-motion' + * + * export const Items = ({ items }) => ( + * + * {items.map(item => ( + * + * ))} + * + * ) + * ``` + * + * You can sequence exit animations throughout a tree using variants. + * + * If a child contains multiple `motion` components with `exit` props, it will only unmount the child + * once all `motion` components have finished animating out. Likewise, any components using + * `usePresence` all need to call `safeToRemove`. + * + * @public + */ +const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", root }) => { + const [isParentPresent, safeToRemove] = create.usePresence(propagate); + /** + * Filter any children that aren't ReactElements. We can only track components + * between renders with a props.key. + */ + const presentChildren = React.useMemo(() => onlyElements(children), [children]); + /** + * Track the keys of the currently rendered children. This is used to + * determine which children are exiting. + */ + const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey); + /** + * If `initial={false}` we only want to pass this to components in the first render. + */ + const isInitialRender = React.useRef(true); + /** + * A ref containing the currently present children. When all exit animations + * are complete, we use this to re-render the component with the latest children + * *committed* rather than the latest children *rendered*. + */ + const pendingPresentChildren = React.useRef(presentChildren); + /** + * Track which exiting children have finished animating out. + */ + const exitComplete = create.useConstant(() => new Map()); + /** + * Save children to render as React state. To ensure this component is concurrent-safe, + * we check for exiting children via an effect. + */ + const [diffedChildren, setDiffedChildren] = React.useState(presentChildren); + const [renderedChildren, setRenderedChildren] = React.useState(presentChildren); + create.useIsomorphicLayoutEffect(() => { + isInitialRender.current = false; + pendingPresentChildren.current = presentChildren; + /** + * Update complete status of exiting children. + */ + for (let i = 0; i < renderedChildren.length; i++) { + const key = getChildKey(renderedChildren[i]); + if (!presentKeys.includes(key)) { + if (exitComplete.get(key) !== true) { + exitComplete.set(key, false); + } + } + else { + exitComplete.delete(key); + } + } + }, [renderedChildren, presentKeys.length, presentKeys.join("-")]); + const exitingChildren = []; + if (presentChildren !== diffedChildren) { + let nextChildren = [...presentChildren]; + /** + * Loop through all the currently rendered components and decide which + * are exiting. + */ + for (let i = 0; i < renderedChildren.length; i++) { + const child = renderedChildren[i]; + const key = getChildKey(child); + if (!presentKeys.includes(key)) { + nextChildren.splice(i, 0, child); + exitingChildren.push(child); + } + } + /** + * If we're in "wait" mode, and we have exiting children, we want to + * only render these until they've all exited. + */ + if (mode === "wait" && exitingChildren.length) { + nextChildren = exitingChildren; + } + setRenderedChildren(onlyElements(nextChildren)); + setDiffedChildren(presentChildren); + /** + * Early return to ensure once we've set state with the latest diffed + * children, we can immediately re-render. + */ + return null; + } + if (process.env.NODE_ENV !== "production" && + mode === "wait" && + renderedChildren.length > 1) { + console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`); + } + /** + * If we've been provided a forceRender function by the LayoutGroupContext, + * we can use it to force a re-render amongst all surrounding components once + * all components have finished animating out. + */ + const { forceRender } = React.useContext(create.LayoutGroupContext); + return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderedChildren.map((child) => { + const key = getChildKey(child); + const isPresent = propagate && !isParentPresent + ? false + : presentChildren === renderedChildren || + presentKeys.includes(key); + const onExit = () => { + if (exitComplete.has(key)) { + exitComplete.set(key, true); + } + else { + return; + } + let isEveryExitComplete = true; + exitComplete.forEach((isExitComplete) => { + if (!isExitComplete) + isEveryExitComplete = false; + }); + if (isEveryExitComplete) { + forceRender?.(); + setRenderedChildren(pendingPresentChildren.current); + propagate && safeToRemove?.(); + onExitComplete && onExitComplete(); + } + }; + return (jsxRuntime.jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial + ? undefined + : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, root: root, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key)); + }) })); +}; + +/** + * Note: Still used by components generated by old versions of Framer + * + * @deprecated + */ +const DeprecatedLayoutGroupContext = React.createContext(null); + +const notify = (node) => !node.isLayoutDirty && node.willUpdate(false); +function nodeGroup() { + const nodes = new Set(); + const subscriptions = new WeakMap(); + const dirtyAll = () => nodes.forEach(notify); + return { + add: (node) => { + nodes.add(node); + subscriptions.set(node, node.addEventListener("willUpdate", dirtyAll)); + }, + remove: (node) => { + nodes.delete(node); + const unsubscribe = subscriptions.get(node); + if (unsubscribe) { + unsubscribe(); + subscriptions.delete(node); + } + dirtyAll(); + }, + dirty: dirtyAll, + }; +} + +function useIsMounted() { + const isMounted = React.useRef(false); + create.useIsomorphicLayoutEffect(() => { + isMounted.current = true; + return () => { + isMounted.current = false; + }; + }, []); + return isMounted; +} + +function useForceUpdate() { + const isMounted = useIsMounted(); + const [forcedRenderCount, setForcedRenderCount] = React.useState(0); + const forceRender = React.useCallback(() => { + isMounted.current && setForcedRenderCount(forcedRenderCount + 1); + }, [forcedRenderCount]); + /** + * Defer this to the end of the next animation frame in case there are multiple + * synchronous calls. + */ + const deferredForceRender = React.useCallback(() => motionDom.frame.postRender(forceRender), [forceRender]); + return [deferredForceRender, forcedRenderCount]; +} + +const shouldInheritGroup = (inherit) => inherit === true; +const shouldInheritId = (inherit) => shouldInheritGroup(inherit === true) || inherit === "id"; +const LayoutGroup = ({ children, id, inherit = true }) => { + const layoutGroupContext = React.useContext(create.LayoutGroupContext); + const deprecatedLayoutGroupContext = React.useContext(DeprecatedLayoutGroupContext); + const [forceRender, key] = useForceUpdate(); + const context = React.useRef(null); + const upstreamId = layoutGroupContext.id || deprecatedLayoutGroupContext; + if (context.current === null) { + if (shouldInheritId(inherit) && upstreamId) { + id = id ? upstreamId + "-" + id : upstreamId; + } + context.current = { + id, + group: shouldInheritGroup(inherit) + ? layoutGroupContext.group || nodeGroup() + : nodeGroup(), + }; + } + const memoizedContext = React.useMemo(() => ({ ...context.current, forceRender }), [key]); + return (jsxRuntime.jsx(create.LayoutGroupContext.Provider, { value: memoizedContext, children: children })); +}; + +/** + * Used in conjunction with the `m` component to reduce bundle size. + * + * `m` is a version of the `motion` component that only loads functionality + * critical for the initial render. + * + * `LazyMotion` can then be used to either synchronously or asynchronously + * load animation and gesture support. + * + * ```jsx + * // Synchronous loading + * import { LazyMotion, m, domAnimation } from "framer-motion" + * + * function App() { + * return ( + * + * + * + * ) + * } + * + * // Asynchronous loading + * import { LazyMotion, m } from "framer-motion" + * + * function App() { + * return ( + * import('./path/to/domAnimation')}> + * + * + * ) + * } + * ``` + * + * @public + */ +function LazyMotion({ children, features, strict = false }) { + const [, setIsLoaded] = React.useState(!isLazyBundle(features)); + const loadedRenderer = React.useRef(undefined); + /** + * If this is a synchronous load, load features immediately + */ + if (!isLazyBundle(features)) { + const { renderer, ...loadedFeatures } = features; + loadedRenderer.current = renderer; + create.loadFeatures(loadedFeatures); + } + React.useEffect(() => { + if (isLazyBundle(features)) { + features().then(({ renderer, ...loadedFeatures }) => { + create.loadFeatures(loadedFeatures); + loadedRenderer.current = renderer; + setIsLoaded(true); + }); + } + }, []); + return (jsxRuntime.jsx(create.LazyContext.Provider, { value: { renderer: loadedRenderer.current, strict }, children: children })); +} +function isLazyBundle(features) { + return typeof features === "function"; +} + +/** + * `MotionConfig` is used to set configuration options for all children `motion` components. + * + * ```jsx + * import { motion, MotionConfig } from "framer-motion" + * + * export function App() { + * return ( + * + * + * + * ) + * } + * ``` + * + * @public + */ +function MotionConfig({ children, isValidProp, ...config }) { + isValidProp && create.loadExternalIsValidProp(isValidProp); + /** + * Inherit props from any parent MotionConfig components + */ + config = { ...React.useContext(create.MotionConfigContext), ...config }; + /** + * Don't allow isStatic to change between renders as it affects how many hooks + * motion components fire. + */ + config.isStatic = create.useConstant(() => config.isStatic); + /** + * Creating a new config context object will re-render every `motion` component + * every time it renders. So we only want to create a new one sparingly. + */ + const context = React.useMemo(() => config, [ + JSON.stringify(config.transition), + config.transformPagePoint, + config.reducedMotion, + ]); + return (jsxRuntime.jsx(create.MotionConfigContext.Provider, { value: context, children: children })); +} + +const ReorderContext = React.createContext(null); + +function createDOMMotionComponentProxy(componentFactory) { + if (typeof Proxy === "undefined") { + return componentFactory; + } + /** + * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc. + * Rather than generating them anew every render. + */ + const componentCache = new Map(); + const deprecatedFactoryFunction = (...args) => { + if (process.env.NODE_ENV !== "production") { + motionUtils.warnOnce(false, "motion() is deprecated. Use motion.create() instead."); + } + return componentFactory(...args); + }; + return new Proxy(deprecatedFactoryFunction, { + /** + * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc. + * The prop name is passed through as `key` and we can use that to generate a `motion` + * DOM component with that name. + */ + get: (_target, key) => { + if (key === "create") + return componentFactory; + /** + * If this element doesn't exist in the component cache, create it and cache. + */ + if (!componentCache.has(key)) { + componentCache.set(key, componentFactory(key)); + } + return componentCache.get(key); + }, + }); +} + +const motion = /*@__PURE__*/ createDOMMotionComponentProxy(create.createMotionComponent); + +function checkReorder(order, value, offset, velocity) { + if (!velocity) + return order; + const index = order.findIndex((item) => item.value === value); + if (index === -1) + return order; + const nextOffset = velocity > 0 ? 1 : -1; + const nextItem = order[index + nextOffset]; + if (!nextItem) + return order; + const item = order[index]; + const nextLayout = nextItem.layout; + const nextItemCenter = motionDom.mixNumber(nextLayout.min, nextLayout.max, 0.5); + if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) || + (nextOffset === -1 && item.layout.min + offset < nextItemCenter)) { + return motionUtils.moveItem(order, index, index + nextOffset); + } + return order; +} + +function ReorderGroupComponent({ children, as = "ul", axis = "y", onReorder, values, ...props }, externalRef) { + const Component = create.useConstant(() => motion[as]); + const order = []; + const isReordering = React.useRef(false); + motionUtils.invariant(Boolean(values), "Reorder.Group must be provided a values prop"); + const context = { + axis, + registerItem: (value, layout) => { + // If the entry was already added, update it rather than adding it again + const idx = order.findIndex((entry) => value === entry.value); + if (idx !== -1) { + order[idx].layout = layout[axis]; + } + else { + order.push({ value: value, layout: layout[axis] }); + } + order.sort(compareMin); + }, + updateOrder: (item, offset, velocity) => { + if (isReordering.current) + return; + const newOrder = checkReorder(order, item, offset, velocity); + if (order !== newOrder) { + isReordering.current = true; + onReorder(newOrder + .map(getValue) + .filter((value) => values.indexOf(value) !== -1)); + } + }, + }; + React.useEffect(() => { + isReordering.current = false; + }); + return (jsxRuntime.jsx(Component, { ...props, ref: externalRef, ignoreStrict: true, children: jsxRuntime.jsx(ReorderContext.Provider, { value: context, children: children }) })); +} +const ReorderGroup = /*@__PURE__*/ React.forwardRef(ReorderGroupComponent); +function getValue(item) { + return item.value; +} +function compareMin(a, b) { + return a.layout.min - b.layout.min; +} + +/** + * Creates a `MotionValue` to track the state and velocity of a value. + * + * Usually, these are created automatically. For advanced use-cases, like use with `useTransform`, you can create `MotionValue`s externally and pass them into the animated component via the `style` prop. + * + * ```jsx + * export const MyComponent = () => { + * const scale = useMotionValue(1) + * + * return + * } + * ``` + * + * @param initial - The initial state. + * + * @public + */ +function useMotionValue(initial) { + const value = create.useConstant(() => motionDom.motionValue(initial)); + /** + * If this motion value is being used in static mode, like on + * the Framer canvas, force components to rerender when the motion + * value is updated. + */ + const { isStatic } = React.useContext(create.MotionConfigContext); + if (isStatic) { + const [, setLatest] = React.useState(initial); + React.useEffect(() => value.on("change", setLatest), []); + } + return value; +} + +function useCombineMotionValues(values, combineValues) { + /** + * Initialise the returned motion value. This remains the same between renders. + */ + const value = useMotionValue(combineValues()); + /** + * Create a function that will update the template motion value with the latest values. + * This is pre-bound so whenever a motion value updates it can schedule its + * execution in Framesync. If it's already been scheduled it won't be fired twice + * in a single frame. + */ + const updateValue = () => value.set(combineValues()); + /** + * Synchronously update the motion value with the latest values during the render. + * This ensures that within a React render, the styles applied to the DOM are up-to-date. + */ + updateValue(); + /** + * Subscribe to all motion values found within the template. Whenever any of them change, + * schedule an update. + */ + create.useIsomorphicLayoutEffect(() => { + const scheduleUpdate = () => motionDom.frame.preRender(updateValue, false, true); + const subscriptions = values.map((v) => v.on("change", scheduleUpdate)); + return () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + motionDom.cancelFrame(updateValue); + }; + }); + return value; +} + +function useComputed(compute) { + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * will be saved into this array. + */ + motionDom.collectMotionValues.current = []; + compute(); + const value = useCombineMotionValues(motionDom.collectMotionValues.current, compute); + /** + * Synchronously close session of collectMotionValues. + */ + motionDom.collectMotionValues.current = undefined; + return value; +} + +function useTransform(input, inputRangeOrTransformer, outputRange, options) { + if (typeof input === "function") { + return useComputed(input); + } + const transformer = typeof inputRangeOrTransformer === "function" + ? inputRangeOrTransformer + : motionDom.transform(inputRangeOrTransformer, outputRange, options); + return Array.isArray(input) + ? useListTransform(input, transformer) + : useListTransform([input], ([latest]) => transformer(latest)); +} +function useListTransform(values, transformer) { + const latest = create.useConstant(() => []); + return useCombineMotionValues(values, () => { + latest.length = 0; + const numValues = values.length; + for (let i = 0; i < numValues; i++) { + latest[i] = values[i].get(); + } + return transformer(latest); + }); +} + +function useDefaultMotionValue(value, defaultValue = 0) { + return motionDom.isMotionValue(value) ? value : useMotionValue(defaultValue); +} +function ReorderItemComponent({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) { + const Component = create.useConstant(() => motion[as]); + const context = React.useContext(ReorderContext); + const point = { + x: useDefaultMotionValue(style.x), + y: useDefaultMotionValue(style.y), + }; + const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset"); + motionUtils.invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group"); + const { axis, registerItem, updateOrder } = context; + return (jsxRuntime.jsx(Component, { drag: axis, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDrag: (event, gesturePoint) => { + const { velocity } = gesturePoint; + velocity[axis] && + updateOrder(value, point[axis].get(), velocity[axis]); + onDrag && onDrag(event, gesturePoint); + }, onLayoutMeasure: (measured) => registerItem(value, measured), ref: externalRef, ignoreStrict: true, children: children })); +} +const ReorderItem = /*@__PURE__*/ React.forwardRef(ReorderItemComponent); + +var namespace = /*#__PURE__*/Object.freeze({ + __proto__: null, + Group: ReorderGroup, + Item: ReorderItem +}); + +function isDOMKeyframes(keyframes) { + return typeof keyframes === "object" && !Array.isArray(keyframes); +} + +function resolveSubjects(subject, keyframes, scope, selectorCache) { + if (typeof subject === "string" && isDOMKeyframes(keyframes)) { + return motionDom.resolveElements(subject, scope, selectorCache); + } + else if (subject instanceof NodeList) { + return Array.from(subject); + } + else if (Array.isArray(subject)) { + return subject; + } + else { + return [subject]; + } +} + +function calculateRepeatDuration(duration, repeat, _repeatDelay) { + return duration * (repeat + 1); +} + +/** + * Given a absolute or relative time definition and current/prev time state of the sequence, + * calculate an absolute time for the next keyframes. + */ +function calcNextTime(current, next, prev, labels) { + if (typeof next === "number") { + return next; + } + else if (next.startsWith("-") || next.startsWith("+")) { + return Math.max(0, current + parseFloat(next)); + } + else if (next === "<") { + return prev; + } + else if (next.startsWith("<")) { + return Math.max(0, prev + parseFloat(next.slice(1))); + } + else { + return labels.get(next) ?? current; + } +} + +function eraseKeyframes(sequence, startTime, endTime) { + for (let i = 0; i < sequence.length; i++) { + const keyframe = sequence[i]; + if (keyframe.at > startTime && keyframe.at < endTime) { + motionUtils.removeItem(sequence, keyframe); + // If we remove this item we have to push the pointer back one + i--; + } + } +} +function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) { + /** + * Erase every existing value between currentTime and targetTime, + * this will essentially splice this timeline into any currently + * defined ones. + */ + eraseKeyframes(sequence, startTime, endTime); + for (let i = 0; i < keyframes.length; i++) { + sequence.push({ + value: keyframes[i], + at: motionDom.mixNumber(startTime, endTime, offset[i]), + easing: motionUtils.getEasingForSegment(easing, i), + }); + } +} + +/** + * Take an array of times that represent repeated keyframes. For instance + * if we have original times of [0, 0.5, 1] then our repeated times will + * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back + * down to a 0-1 scale. + */ +function normalizeTimes(times, repeat) { + for (let i = 0; i < times.length; i++) { + times[i] = times[i] / (repeat + 1); + } +} + +function compareByTime(a, b) { + if (a.at === b.at) { + if (a.value === null) + return 1; + if (b.value === null) + return -1; + return 0; + } + else { + return a.at - b.at; + } +} + +const defaultSegmentEasing = "easeInOut"; +const MAX_REPEAT = 20; +function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) { + const defaultDuration = defaultTransition.duration || 0.3; + const animationDefinitions = new Map(); + const sequences = new Map(); + const elementCache = {}; + const timeLabels = new Map(); + let prevTime = 0; + let currentTime = 0; + let totalDuration = 0; + /** + * Build the timeline by mapping over the sequence array and converting + * the definitions into keyframes and offsets with absolute time values. + * These will later get converted into relative offsets in a second pass. + */ + for (let i = 0; i < sequence.length; i++) { + const segment = sequence[i]; + /** + * If this is a timeline label, mark it and skip the rest of this iteration. + */ + if (typeof segment === "string") { + timeLabels.set(segment, currentTime); + continue; + } + else if (!Array.isArray(segment)) { + timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels)); + continue; + } + let [subject, keyframes, transition = {}] = segment; + /** + * If a relative or absolute time value has been specified we need to resolve + * it in relation to the currentTime. + */ + if (transition.at !== undefined) { + currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels); + } + /** + * Keep track of the maximum duration in this definition. This will be + * applied to currentTime once the definition has been parsed. + */ + let maxDuration = 0; + const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => { + const valueKeyframesAsList = keyframesAsList(valueKeyframes); + const { delay = 0, times = motionDom.defaultOffset(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition; + let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition; + /** + * Resolve stagger() if defined. + */ + const calculatedDelay = typeof delay === "function" + ? delay(elementIndex, numSubjects) + : delay; + /** + * If this animation should and can use a spring, generate a spring easing function. + */ + const numKeyframes = valueKeyframesAsList.length; + const createGenerator = motionDom.isGenerator(type) + ? type + : generators?.[type || "keyframes"]; + if (numKeyframes <= 2 && createGenerator) { + /** + * As we're creating an easing function from a spring, + * ideally we want to generate it using the real distance + * between the two keyframes. However this isn't always + * possible - in these situations we use 0-100. + */ + let absoluteDelta = 100; + if (numKeyframes === 2 && + isNumberKeyframesArray(valueKeyframesAsList)) { + const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0]; + absoluteDelta = Math.abs(delta); + } + const springTransition = { ...remainingTransition }; + if (duration !== undefined) { + springTransition.duration = motionUtils.secondsToMilliseconds(duration); + } + const springEasing = motionDom.createGeneratorEasing(springTransition, absoluteDelta, createGenerator); + ease = springEasing.ease; + duration = springEasing.duration; + } + duration ?? (duration = defaultDuration); + const startTime = currentTime + calculatedDelay; + /** + * If there's only one time offset of 0, fill in a second with length 1 + */ + if (times.length === 1 && times[0] === 0) { + times[1] = 1; + } + /** + * Fill out if offset if fewer offsets than keyframes + */ + const remainder = times.length - valueKeyframesAsList.length; + remainder > 0 && motionDom.fillOffset(times, remainder); + /** + * If only one value has been set, ie [1], push a null to the start of + * the keyframe array. This will let us mark a keyframe at this point + * that will later be hydrated with the previous value. + */ + valueKeyframesAsList.length === 1 && + valueKeyframesAsList.unshift(null); + /** + * Handle repeat options + */ + if (repeat) { + motionUtils.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20"); + duration = calculateRepeatDuration(duration, repeat); + const originalKeyframes = [...valueKeyframesAsList]; + const originalTimes = [...times]; + ease = Array.isArray(ease) ? [...ease] : [ease]; + const originalEase = [...ease]; + for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { + valueKeyframesAsList.push(...originalKeyframes); + for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) { + times.push(originalTimes[keyframeIndex] + (repeatIndex + 1)); + ease.push(keyframeIndex === 0 + ? "linear" + : motionUtils.getEasingForSegment(originalEase, keyframeIndex - 1)); + } + } + normalizeTimes(times, repeat); + } + const targetTime = startTime + duration; + /** + * Add keyframes, mapping offsets to absolute time. + */ + addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime); + maxDuration = Math.max(calculatedDelay + duration, maxDuration); + totalDuration = Math.max(targetTime, totalDuration); + }; + if (motionDom.isMotionValue(subject)) { + const subjectSequence = getSubjectSequence(subject, sequences); + resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope, elementCache); + const numSubjects = subjects.length; + /** + * For every element in this segment, process the defined values. + */ + for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) { + /** + * Cast necessary, but we know these are of this type + */ + keyframes = keyframes; + transition = transition; + const thisSubject = subjects[subjectIndex]; + const subjectSequence = getSubjectSequence(thisSubject, sequences); + for (const key in keyframes) { + resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects); + } + } + } + prevTime = currentTime; + currentTime += maxDuration; + } + /** + * For every element and value combination create a new animation. + */ + sequences.forEach((valueSequences, element) => { + for (const key in valueSequences) { + const valueSequence = valueSequences[key]; + /** + * Arrange all the keyframes in ascending time order. + */ + valueSequence.sort(compareByTime); + const keyframes = []; + const valueOffset = []; + const valueEasing = []; + /** + * For each keyframe, translate absolute times into + * relative offsets based on the total duration of the timeline. + */ + for (let i = 0; i < valueSequence.length; i++) { + const { at, value, easing } = valueSequence[i]; + keyframes.push(value); + valueOffset.push(motionUtils.progress(0, totalDuration, at)); + valueEasing.push(easing || "easeOut"); + } + /** + * If the first keyframe doesn't land on offset: 0 + * provide one by duplicating the initial keyframe. This ensures + * it snaps to the first keyframe when the animation starts. + */ + if (valueOffset[0] !== 0) { + valueOffset.unshift(0); + keyframes.unshift(keyframes[0]); + valueEasing.unshift(defaultSegmentEasing); + } + /** + * If the last keyframe doesn't land on offset: 1 + * provide one with a null wildcard value. This will ensure it + * stays static until the end of the animation. + */ + if (valueOffset[valueOffset.length - 1] !== 1) { + valueOffset.push(1); + keyframes.push(null); + } + if (!animationDefinitions.has(element)) { + animationDefinitions.set(element, { + keyframes: {}, + transition: {}, + }); + } + const definition = animationDefinitions.get(element); + definition.keyframes[key] = keyframes; + definition.transition[key] = { + ...defaultTransition, + duration: totalDuration, + ease: valueEasing, + times: valueOffset, + ...sequenceTransition, + }; + } + }); + return animationDefinitions; +} +function getSubjectSequence(subject, sequences) { + !sequences.has(subject) && sequences.set(subject, {}); + return sequences.get(subject); +} +function getValueSequence(name, sequences) { + if (!sequences[name]) + sequences[name] = []; + return sequences[name]; +} +function keyframesAsList(keyframes) { + return Array.isArray(keyframes) ? keyframes : [keyframes]; +} +function getValueTransition(transition, key) { + return transition && transition[key] + ? { + ...transition, + ...transition[key], + } + : { ...transition }; +} +const isNumber = (keyframe) => typeof keyframe === "number"; +const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber); + +function isObjectKey(key, object) { + return key in object; +} +class ObjectVisualElement extends create.VisualElement { + constructor() { + super(...arguments); + this.type = "object"; + } + readValueFromInstance(instance, key) { + if (isObjectKey(key, instance)) { + const value = instance[key]; + if (typeof value === "string" || typeof value === "number") { + return value; + } + } + return undefined; + } + getBaseTargetFromProps() { + return undefined; + } + removeValueFromRenderState(key, renderState) { + delete renderState.output[key]; + } + measureInstanceViewportBox() { + return create.createBox(); + } + build(renderState, latestValues) { + Object.assign(renderState.output, latestValues); + } + renderInstance(instance, { output }) { + Object.assign(instance, output); + } + sortInstanceNodePosition() { + return 0; + } +} + +function createDOMVisualElement(element) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + transform: {}, + transformOrigin: {}, + style: {}, + vars: {}, + attrs: {}, + }, + latestValues: {}, + }, + }; + const node = motionDom.isSVGElement(element) && !motionDom.isSVGSVGElement(element) + ? new create.SVGVisualElement(options) + : new create.HTMLVisualElement(options); + node.mount(element); + create.visualElementStore.set(element, node); +} +function createObjectVisualElement(subject) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + output: {}, + }, + latestValues: {}, + }, + }; + const node = new ObjectVisualElement(options); + node.mount(subject); + create.visualElementStore.set(subject, node); +} + +function isSingleValue(subject, keyframes) { + return (motionDom.isMotionValue(subject) || + typeof subject === "number" || + (typeof subject === "string" && !isDOMKeyframes(keyframes))); +} +/** + * Implementation + */ +function animateSubject(subject, keyframes, options, scope) { + const animations = []; + if (isSingleValue(subject, keyframes)) { + animations.push(create.animateSingleValue(subject, isDOMKeyframes(keyframes) + ? keyframes.default || keyframes + : keyframes, options ? options.default || options : options)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope); + const numSubjects = subjects.length; + motionUtils.invariant(Boolean(numSubjects), "No valid elements provided."); + for (let i = 0; i < numSubjects; i++) { + const thisSubject = subjects[i]; + const createVisualElement = thisSubject instanceof Element + ? createDOMVisualElement + : createObjectVisualElement; + if (!create.visualElementStore.has(thisSubject)) { + createVisualElement(thisSubject); + } + const visualElement = create.visualElementStore.get(thisSubject); + const transition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if ("delay" in transition && + typeof transition.delay === "function") { + transition.delay = transition.delay(i, numSubjects); + } + animations.push(...create.animateTarget(visualElement, { ...keyframes, transition }, {})); + } + } + return animations; +} + +function animateSequence(sequence, options, scope) { + const animations = []; + const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring: motionDom.spring }); + animationDefinitions.forEach(({ keyframes, transition }, subject) => { + animations.push(...animateSubject(subject, keyframes, transition)); + }); + return animations; +} + +function isSequence(value) { + return Array.isArray(value) && value.some(Array.isArray); +} +/** + * Creates an animation function that is optionally scoped + * to a specific element. + */ +function createScopedAnimate(scope) { + /** + * Implementation + */ + function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) { + let animations = []; + if (isSequence(subjectOrSequence)) { + animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope); + } + else { + animations = animateSubject(subjectOrSequence, optionsOrKeyframes, options, scope); + } + const animation = new motionDom.GroupAnimationWithThen(animations); + if (scope) { + scope.animations.push(animation); + animation.finished.then(() => { + motionUtils.removeItem(scope.animations, animation); + }); + } + return animation; + } + return scopedAnimate; +} +const animate = createScopedAnimate(); + +function animateElements(elementOrSelector, keyframes, options, scope) { + const elements = motionDom.resolveElements(elementOrSelector, scope); + const numElements = elements.length; + motionUtils.invariant(Boolean(numElements), "No valid element provided."); + /** + * WAAPI doesn't support interrupting animations. + * + * Therefore, starting animations requires a three-step process: + * 1. Stop existing animations (write styles to DOM) + * 2. Resolve keyframes (read styles from DOM) + * 3. Create new animations (write styles to DOM) + * + * The hybrid `animate()` function uses AsyncAnimation to resolve + * keyframes before creating new animations, which removes style + * thrashing. Here, we have much stricter filesize constraints. + * Therefore we do this in a synchronous way that ensures that + * at least within `animate()` calls there is no style thrashing. + * + * In the motion-native-animate-mini-interrupt benchmark this + * was 80% faster than a single loop. + */ + const animationDefinitions = []; + /** + * Step 1: Build options and stop existing animations (write) + */ + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + const elementTransition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if (typeof elementTransition.delay === "function") { + elementTransition.delay = elementTransition.delay(i, numElements); + } + for (const valueName in keyframes) { + let valueKeyframes = keyframes[valueName]; + if (!Array.isArray(valueKeyframes)) { + valueKeyframes = [valueKeyframes]; + } + const valueOptions = { + ...motionDom.getValueTransition(elementTransition, valueName), + }; + valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay)); + /** + * If there's an existing animation playing on this element then stop it + * before creating a new one. + */ + const map = motionDom.getAnimationMap(element); + const key = motionDom.animationMapKey(valueName, valueOptions.pseudoElement || ""); + const currentAnimation = map.get(key); + currentAnimation && currentAnimation.stop(); + animationDefinitions.push({ + map, + key, + unresolvedKeyframes: valueKeyframes, + options: { + ...valueOptions, + element, + name: valueName, + allowFlatten: !elementTransition.type && !elementTransition.ease, + }, + }); + } + } + /** + * Step 2: Resolve keyframes (read) + */ + for (let i = 0; i < animationDefinitions.length; i++) { + const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i]; + const { element, name, pseudoElement } = animationOptions; + if (!pseudoElement && unresolvedKeyframes[0] === null) { + unresolvedKeyframes[0] = motionDom.getComputedStyle(element, name); + } + motionDom.fillWildcards(unresolvedKeyframes); + motionDom.applyPxDefaults(unresolvedKeyframes, name); + /** + * If we only have one keyframe, explicitly read the initial keyframe + * from the computed style. This is to ensure consistency with WAAPI behaviour + * for restarting animations, for instance .play() after finish, when it + * has one vs two keyframes. + */ + if (!pseudoElement && unresolvedKeyframes.length < 2) { + unresolvedKeyframes.unshift(motionDom.getComputedStyle(element, name)); + } + animationOptions.keyframes = unresolvedKeyframes; + } + /** + * Step 3: Create new animations (write) + */ + const animations = []; + for (let i = 0; i < animationDefinitions.length; i++) { + const { map, key, options: animationOptions } = animationDefinitions[i]; + const animation = new motionDom.NativeAnimation(animationOptions); + map.set(key, animation); + animation.finished.finally(() => map.delete(key)); + animations.push(animation); + } + return animations; +} + +const createScopedWaapiAnimate = (scope) => { + function scopedAnimate(elementOrSelector, keyframes, options) { + return new motionDom.GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope)); + } + return scopedAnimate; +}; +const animateMini = /*@__PURE__*/ createScopedWaapiAnimate(); + +/** + * A time in milliseconds, beyond which we consider the scroll velocity to be 0. + */ +const maxElapsed = 50; +const createAxisInfo = () => ({ + current: 0, + offset: [], + progress: 0, + scrollLength: 0, + targetOffset: 0, + targetLength: 0, + containerLength: 0, + velocity: 0, +}); +const createScrollInfo = () => ({ + time: 0, + x: createAxisInfo(), + y: createAxisInfo(), +}); +const keys = { + x: { + length: "Width", + position: "Left", + }, + y: { + length: "Height", + position: "Top", + }, +}; +function updateAxisInfo(element, axisName, info, time) { + const axis = info[axisName]; + const { length, position } = keys[axisName]; + const prev = axis.current; + const prevTime = info.time; + axis.current = element[`scroll${position}`]; + axis.scrollLength = element[`scroll${length}`] - element[`client${length}`]; + axis.offset.length = 0; + axis.offset[0] = 0; + axis.offset[1] = axis.scrollLength; + axis.progress = motionUtils.progress(0, axis.scrollLength, axis.current); + const elapsed = time - prevTime; + axis.velocity = + elapsed > maxElapsed + ? 0 + : motionUtils.velocityPerSecond(axis.current - prev, elapsed); +} +function updateScrollInfo(element, info, time) { + updateAxisInfo(element, "x", info, time); + updateAxisInfo(element, "y", info, time); + info.time = time; +} + +function calcInset(element, container) { + const inset = { x: 0, y: 0 }; + let current = element; + while (current && current !== container) { + if (motionDom.isHTMLElement(current)) { + inset.x += current.offsetLeft; + inset.y += current.offsetTop; + current = current.offsetParent; + } + else if (current.tagName === "svg") { + /** + * This isn't an ideal approach to measuring the offset of tags. + * It would be preferable, given they behave like HTMLElements in most ways + * to use offsetLeft/Top. But these don't exist on . Likewise we + * can't use .getBBox() like most SVG elements as these provide the offset + * relative to the SVG itself, which for is usually 0x0. + */ + const svgBoundingBox = current.getBoundingClientRect(); + current = current.parentElement; + const parentBoundingBox = current.getBoundingClientRect(); + inset.x += svgBoundingBox.left - parentBoundingBox.left; + inset.y += svgBoundingBox.top - parentBoundingBox.top; + } + else if (current instanceof SVGGraphicsElement) { + const { x, y } = current.getBBox(); + inset.x += x; + inset.y += y; + let svg = null; + let parent = current.parentNode; + while (!svg) { + if (parent.tagName === "svg") { + svg = parent; + } + parent = current.parentNode; + } + current = svg; + } + else { + break; + } + } + return inset; +} + +const namedEdges = { + start: 0, + center: 0.5, + end: 1, +}; +function resolveEdge(edge, length, inset = 0) { + let delta = 0; + /** + * If we have this edge defined as a preset, replace the definition + * with the numerical value. + */ + if (edge in namedEdges) { + edge = namedEdges[edge]; + } + /** + * Handle unit values + */ + if (typeof edge === "string") { + const asNumber = parseFloat(edge); + if (edge.endsWith("px")) { + delta = asNumber; + } + else if (edge.endsWith("%")) { + edge = asNumber / 100; + } + else if (edge.endsWith("vw")) { + delta = (asNumber / 100) * document.documentElement.clientWidth; + } + else if (edge.endsWith("vh")) { + delta = (asNumber / 100) * document.documentElement.clientHeight; + } + else { + edge = asNumber; + } + } + /** + * If the edge is defined as a number, handle as a progress value. + */ + if (typeof edge === "number") { + delta = length * edge; + } + return inset + delta; +} + +const defaultOffset = [0, 0]; +function resolveOffset(offset, containerLength, targetLength, targetInset) { + let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset; + let targetPoint = 0; + let containerPoint = 0; + if (typeof offset === "number") { + /** + * If we're provided offset: [0, 0.5, 1] then each number x should become + * [x, x], so we default to the behaviour of mapping 0 => 0 of both target + * and container etc. + */ + offsetDefinition = [offset, offset]; + } + else if (typeof offset === "string") { + offset = offset.trim(); + if (offset.includes(" ")) { + offsetDefinition = offset.split(" "); + } + else { + /** + * If we're provided a definition like "100px" then we want to apply + * that only to the top of the target point, leaving the container at 0. + * Whereas a named offset like "end" should be applied to both. + */ + offsetDefinition = [offset, namedEdges[offset] ? offset : `0`]; + } + } + targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset); + containerPoint = resolveEdge(offsetDefinition[1], containerLength); + return targetPoint - containerPoint; +} + +const ScrollOffset = { + Enter: [ + [0, 1], + [1, 1], + ], + Exit: [ + [0, 0], + [1, 0], + ], + Any: [ + [1, 0], + [0, 1], + ], + All: [ + [0, 0], + [1, 1], + ], +}; + +const point = { x: 0, y: 0 }; +function getTargetSize(target) { + return "getBBox" in target && target.tagName !== "svg" + ? target.getBBox() + : { width: target.clientWidth, height: target.clientHeight }; +} +function resolveOffsets(container, info, options) { + const { offset: offsetDefinition = ScrollOffset.All } = options; + const { target = container, axis = "y" } = options; + const lengthLabel = axis === "y" ? "height" : "width"; + const inset = target !== container ? calcInset(target, container) : point; + /** + * Measure the target and container. If they're the same thing then we + * use the container's scrollWidth/Height as the target, from there + * all other calculations can remain the same. + */ + const targetSize = target === container + ? { width: container.scrollWidth, height: container.scrollHeight } + : getTargetSize(target); + const containerSize = { + width: container.clientWidth, + height: container.clientHeight, + }; + /** + * Reset the length of the resolved offset array rather than creating a new one. + * TODO: More reusable data structures for targetSize/containerSize would also be good. + */ + info[axis].offset.length = 0; + /** + * Populate the offset array by resolving the user's offset definition into + * a list of pixel scroll offets. + */ + let hasChanged = !info[axis].interpolate; + const numOffsets = offsetDefinition.length; + for (let i = 0; i < numOffsets; i++) { + const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]); + if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) { + hasChanged = true; + } + info[axis].offset[i] = offset; + } + /** + * If the pixel scroll offsets have changed, create a new interpolator function + * to map scroll value into a progress. + */ + if (hasChanged) { + info[axis].interpolate = motionDom.interpolate(info[axis].offset, motionDom.defaultOffset(offsetDefinition), { clamp: false }); + info[axis].interpolatorOffsets = [...info[axis].offset]; + } + info[axis].progress = motionUtils.clamp(0, 1, info[axis].interpolate(info[axis].current)); +} + +function measure(container, target = container, info) { + /** + * Find inset of target within scrollable container + */ + info.x.targetOffset = 0; + info.y.targetOffset = 0; + if (target !== container) { + let node = target; + while (node && node !== container) { + info.x.targetOffset += node.offsetLeft; + info.y.targetOffset += node.offsetTop; + node = node.offsetParent; + } + } + info.x.targetLength = + target === container ? target.scrollWidth : target.clientWidth; + info.y.targetLength = + target === container ? target.scrollHeight : target.clientHeight; + info.x.containerLength = container.clientWidth; + info.y.containerLength = container.clientHeight; + /** + * In development mode ensure scroll containers aren't position: static as this makes + * it difficult to measure their relative positions. + */ + if (process.env.NODE_ENV !== "production") { + if (container && target && target !== container) { + motionUtils.warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly."); + } + } +} +function createOnScrollHandler(element, onScroll, info, options = {}) { + return { + measure: (time) => { + measure(element, options.target, info); + updateScrollInfo(element, info, time); + if (options.offset || options.target) { + resolveOffsets(element, info, options); + } + }, + notify: () => onScroll(info), + }; +} + +const scrollListeners = new WeakMap(); +const resizeListeners = new WeakMap(); +const onScrollHandlers = new WeakMap(); +const getEventTarget = (element) => element === document.scrollingElement ? window : element; +function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) { + if (!container) + return motionUtils.noop; + let containerHandlers = onScrollHandlers.get(container); + /** + * Get the onScroll handlers for this container. + * If one isn't found, create a new one. + */ + if (!containerHandlers) { + containerHandlers = new Set(); + onScrollHandlers.set(container, containerHandlers); + } + /** + * Create a new onScroll handler for the provided callback. + */ + const info = createScrollInfo(); + const containerHandler = createOnScrollHandler(container, onScroll, info, options); + containerHandlers.add(containerHandler); + /** + * Check if there's a scroll event listener for this container. + * If not, create one. + */ + if (!scrollListeners.has(container)) { + const measureAll = () => { + for (const handler of containerHandlers) { + handler.measure(motionDom.frameData.timestamp); + } + motionDom.frame.preUpdate(notifyAll); + }; + const notifyAll = () => { + for (const handler of containerHandlers) { + handler.notify(); + } + }; + const listener = () => motionDom.frame.read(measureAll); + scrollListeners.set(container, listener); + const target = getEventTarget(container); + window.addEventListener("resize", listener, { passive: true }); + if (container !== document.documentElement) { + resizeListeners.set(container, motionDom.resize(container, listener)); + } + target.addEventListener("scroll", listener, { passive: true }); + listener(); + } + const listener = scrollListeners.get(container); + motionDom.frame.read(listener, false, true); + return () => { + motionDom.cancelFrame(listener); + /** + * Check if we even have any handlers for this container. + */ + const currentHandlers = onScrollHandlers.get(container); + if (!currentHandlers) + return; + currentHandlers.delete(containerHandler); + if (currentHandlers.size) + return; + /** + * If no more handlers, remove the scroll listener too. + */ + const scrollListener = scrollListeners.get(container); + scrollListeners.delete(container); + if (scrollListener) { + getEventTarget(container).removeEventListener("scroll", scrollListener); + resizeListeners.get(container)?.(); + window.removeEventListener("resize", scrollListener); + } + }; +} + +const timelineCache = new Map(); +function scrollTimelineFallback(options) { + const currentTime = { value: 0 }; + const cancel = scrollInfo((info) => { + currentTime.value = info[options.axis].progress * 100; + }, options); + return { currentTime, cancel }; +} +function getTimeline({ source, container, ...options }) { + const { axis } = options; + if (source) + container = source; + const containerCache = timelineCache.get(container) ?? new Map(); + timelineCache.set(container, containerCache); + const targetKey = options.target ?? "self"; + const targetCache = containerCache.get(targetKey) ?? {}; + const axisKey = axis + (options.offset ?? []).join(","); + if (!targetCache[axisKey]) { + targetCache[axisKey] = + !options.target && motionDom.supportsScrollTimeline() + ? new ScrollTimeline({ source: container, axis }) + : scrollTimelineFallback({ container, ...options }); + } + return targetCache[axisKey]; +} + +function attachToAnimation(animation, options) { + const timeline = getTimeline(options); + return animation.attachTimeline({ + timeline: options.target ? undefined : timeline, + observe: (valueAnimation) => { + valueAnimation.pause(); + return motionDom.observeTimeline((progress) => { + valueAnimation.time = valueAnimation.duration * progress; + }, timeline); + }, + }); +} + +/** + * If the onScroll function has two arguments, it's expecting + * more specific information about the scroll from scrollInfo. + */ +function isOnScrollWithInfo(onScroll) { + return onScroll.length === 2; +} +function attachToFunction(onScroll, options) { + if (isOnScrollWithInfo(onScroll)) { + return scrollInfo((info) => { + onScroll(info[options.axis].progress, info); + }, options); + } + else { + return motionDom.observeTimeline(onScroll, getTimeline(options)); + } +} + +function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) { + if (!container) + return motionUtils.noop; + const optionsWithDefaults = { axis, container, ...options }; + return typeof onScroll === "function" + ? attachToFunction(onScroll, optionsWithDefaults) + : attachToAnimation(onScroll, optionsWithDefaults); +} + +const thresholds = { + some: 0, + all: 1, +}; +function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) { + const elements = motionDom.resolveElements(elementOrSelector); + const activeIntersections = new WeakMap(); + const onIntersectionChange = (entries) => { + entries.forEach((entry) => { + const onEnd = activeIntersections.get(entry.target); + /** + * If there's no change to the intersection, we don't need to + * do anything here. + */ + if (entry.isIntersecting === Boolean(onEnd)) + return; + if (entry.isIntersecting) { + const newOnEnd = onStart(entry.target, entry); + if (typeof newOnEnd === "function") { + activeIntersections.set(entry.target, newOnEnd); + } + else { + observer.unobserve(entry.target); + } + } + else if (typeof onEnd === "function") { + onEnd(entry); + activeIntersections.delete(entry.target); + } + }); + }; + const observer = new IntersectionObserver(onIntersectionChange, { + root, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholds[amount], + }); + elements.forEach((element) => observer.observe(element)); + return () => observer.disconnect(); +} + +const createMinimalMotionComponent = +/*@__PURE__*/ create.createMotionComponentFactory(); + +const m = /*@__PURE__*/ createDOMMotionComponentProxy(createMinimalMotionComponent); + +function useUnmountEffect(callback) { + return React.useEffect(() => () => callback(), []); +} + +/** + * @public + */ +const domAnimation = { + renderer: create.createDomVisualElement, + ...create.animations, + ...create.gestureAnimations, +}; + +/** + * @public + */ +const domMax = { + ...domAnimation, + ...create.drag, + ...create.layout, +}; + +/** + * @public + */ +const domMin = { + renderer: create.createDomVisualElement, + ...create.animations, +}; + +function useMotionValueEvent(value, event, callback) { + /** + * useInsertionEffect will create subscriptions before any other + * effects will run. Effects run upwards through the tree so it + * can be that binding a useLayoutEffect higher up the tree can + * miss changes from lower down the tree. + */ + React.useInsertionEffect(() => value.on(event, callback), [value, event, callback]); +} + +function refWarning(name, ref) { + motionUtils.warning(Boolean(!ref || ref.current), `You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`); +} +const createScrollMotionValues = () => ({ + scrollX: motionDom.motionValue(0), + scrollY: motionDom.motionValue(0), + scrollXProgress: motionDom.motionValue(0), + scrollYProgress: motionDom.motionValue(0), +}); +function useScroll({ container, target, layoutEffect = true, ...options } = {}) { + const values = create.useConstant(createScrollMotionValues); + const useLifecycleEffect = layoutEffect + ? create.useIsomorphicLayoutEffect + : React.useEffect; + useLifecycleEffect(() => { + refWarning("target", target); + refWarning("container", container); + return scroll((_progress, { x, y, }) => { + values.scrollX.set(x.current); + values.scrollXProgress.set(x.progress); + values.scrollY.set(y.current); + values.scrollYProgress.set(y.progress); + }, { + ...options, + container: container?.current || undefined, + target: target?.current || undefined, + }); + }, [container, target, JSON.stringify(options.offset)]); + return values; +} + +/** + * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref }) + */ +function useElementScroll(ref) { + if (process.env.NODE_ENV === "development") { + motionUtils.warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref })."); + } + return useScroll({ container: ref }); +} + +/** + * @deprecated useViewportScroll is deprecated. Convert to useScroll() + */ +function useViewportScroll() { + if (process.env.NODE_ENV !== "production") { + motionUtils.warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll()."); + } + return useScroll(); +} + +/** + * Combine multiple motion values into a new one using a string template literal. + * + * ```jsx + * import { + * motion, + * useSpring, + * useMotionValue, + * useMotionTemplate + * } from "framer-motion" + * + * function Component() { + * const shadowX = useSpring(0) + * const shadowY = useMotionValue(0) + * const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))` + * + * return + * } + * ``` + * + * @public + */ +function useMotionTemplate(fragments, ...values) { + /** + * Create a function that will build a string from the latest motion values. + */ + const numFragments = fragments.length; + function buildValue() { + let output = ``; + for (let i = 0; i < numFragments; i++) { + output += fragments[i]; + const value = values[i]; + if (value) { + output += motionDom.isMotionValue(value) ? value.get() : value; + } + } + return output; + } + return useCombineMotionValues(values.filter(motionDom.isMotionValue), buildValue); +} + +function useSpring(source, options = {}) { + const { isStatic } = React.useContext(create.MotionConfigContext); + const getFromSource = () => (motionDom.isMotionValue(source) ? source.get() : source); + // isStatic will never change, allowing early hooks return + if (isStatic) { + return useTransform(getFromSource); + } + const value = useMotionValue(getFromSource()); + React.useInsertionEffect(() => { + return motionDom.attachSpring(value, source, options); + }, [value, JSON.stringify(options)]); + return value; +} + +function useAnimationFrame(callback) { + const initialTimestamp = React.useRef(0); + const { isStatic } = React.useContext(create.MotionConfigContext); + React.useEffect(() => { + if (isStatic) + return; + const provideTimeSinceStart = ({ timestamp, delta }) => { + if (!initialTimestamp.current) + initialTimestamp.current = timestamp; + callback(timestamp - initialTimestamp.current, delta); + }; + motionDom.frame.update(provideTimeSinceStart, true); + return () => motionDom.cancelFrame(provideTimeSinceStart); + }, [callback]); +} + +function useTime() { + const time = useMotionValue(0); + useAnimationFrame((t) => time.set(t)); + return time; +} + +/** + * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes. + * + * ```javascript + * const x = useMotionValue(0) + * const xVelocity = useVelocity(x) + * const xAcceleration = useVelocity(xVelocity) + * ``` + * + * @public + */ +function useVelocity(value) { + const velocity = useMotionValue(value.getVelocity()); + const updateVelocity = () => { + const latest = value.getVelocity(); + velocity.set(latest); + /** + * If we still have velocity, schedule an update for the next frame + * to keep checking until it is zero. + */ + if (latest) + motionDom.frame.update(updateVelocity); + }; + useMotionValueEvent(value, "change", () => { + // Schedule an update to this value at the end of the current frame. + motionDom.frame.update(updateVelocity, false, true); + }); + return velocity; +} + +class WillChangeMotionValue extends motionDom.MotionValue { + constructor() { + super(...arguments); + this.isEnabled = false; + } + add(name) { + if (motionDom.transformProps.has(name) || motionDom.acceleratedValues.has(name)) { + this.isEnabled = true; + this.update(); + } + } + update() { + this.set(this.isEnabled ? "transform" : "auto"); + } +} + +function useWillChange() { + return create.useConstant(() => new WillChangeMotionValue("auto")); +} + +/** + * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting. + * + * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing + * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. + * + * It will actively respond to changes and re-render your components with the latest setting. + * + * ```jsx + * export function Sidebar({ isOpen }) { + * const shouldReduceMotion = useReducedMotion() + * const closedX = shouldReduceMotion ? 0 : "-100%" + * + * return ( + * + * ) + * } + * ``` + * + * @return boolean + * + * @public + */ +function useReducedMotion() { + /** + * Lazy initialisation of prefersReducedMotion + */ + !create.hasReducedMotionListener.current && create.initPrefersReducedMotion(); + const [shouldReduceMotion] = React.useState(create.prefersReducedMotion.current); + if (process.env.NODE_ENV !== "production") { + motionUtils.warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + /** + * TODO See if people miss automatically updating shouldReduceMotion setting + */ + return shouldReduceMotion; +} + +function useReducedMotionConfig() { + const reducedMotionPreference = useReducedMotion(); + const { reducedMotion } = React.useContext(create.MotionConfigContext); + if (reducedMotion === "never") { + return false; + } + else if (reducedMotion === "always") { + return true; + } + else { + return reducedMotionPreference; + } +} + +function stopAnimation(visualElement) { + visualElement.values.forEach((value) => value.stop()); +} +function setVariants(visualElement, variantLabels) { + const reversedLabels = [...variantLabels].reverse(); + reversedLabels.forEach((key) => { + const variant = visualElement.getVariant(key); + variant && create.setTarget(visualElement, variant); + if (visualElement.variantChildren) { + visualElement.variantChildren.forEach((child) => { + setVariants(child, variantLabels); + }); + } + }); +} +function setValues(visualElement, definition) { + if (Array.isArray(definition)) { + return setVariants(visualElement, definition); + } + else if (typeof definition === "string") { + return setVariants(visualElement, [definition]); + } + else { + create.setTarget(visualElement, definition); + } +} +/** + * @public + */ +function animationControls() { + /** + * Track whether the host component has mounted. + */ + let hasMounted = false; + /** + * A collection of linked component animation controls. + */ + const subscribers = new Set(); + const controls = { + subscribe(visualElement) { + subscribers.add(visualElement); + return () => void subscribers.delete(visualElement); + }, + start(definition, transitionOverride) { + motionUtils.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook."); + const animations = []; + subscribers.forEach((visualElement) => { + animations.push(create.animateVisualElement(visualElement, definition, { + transitionOverride, + })); + }); + return Promise.all(animations); + }, + set(definition) { + motionUtils.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook."); + return subscribers.forEach((visualElement) => { + setValues(visualElement, definition); + }); + }, + stop() { + subscribers.forEach((visualElement) => { + stopAnimation(visualElement); + }); + }, + mount() { + hasMounted = true; + return () => { + hasMounted = false; + controls.stop(); + }; + }, + }; + return controls; +} + +function useAnimate() { + const scope = create.useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = create.useConstant(() => createScopedAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + scope.animations.length = 0; + }); + return [scope, animate]; +} + +function useAnimateMini() { + const scope = create.useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = create.useConstant(() => createScopedWaapiAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + }); + return [scope, animate]; +} + +/** + * Creates `LegacyAnimationControls`, which can be used to manually start, stop + * and sequence animations on one or more components. + * + * The returned `LegacyAnimationControls` should be passed to the `animate` property + * of the components you want to animate. + * + * These components can then be animated with the `start` method. + * + * ```jsx + * import * as React from 'react' + * import { motion, useAnimation } from 'framer-motion' + * + * export function MyComponent(props) { + * const controls = useAnimation() + * + * controls.start({ + * x: 100, + * transition: { duration: 0.5 }, + * }) + * + * return + * } + * ``` + * + * @returns Animation controller with `start` and `stop` methods + * + * @public + */ +function useAnimationControls() { + const controls = create.useConstant(animationControls); + create.useIsomorphicLayoutEffect(controls.mount, []); + return controls; +} +const useAnimation = useAnimationControls; + +function usePresenceData() { + const context = React.useContext(create.PresenceContext); + return context ? context.custom : undefined; +} + +/** + * Attaches an event listener directly to the provided DOM element. + * + * Bypassing React's event system can be desirable, for instance when attaching non-passive + * event handlers. + * + * ```jsx + * const ref = useRef(null) + * + * useDomEvent(ref, 'wheel', onWheel, { passive: false }) + * + * return
+ * ``` + * + * @param ref - React.RefObject that's been provided to the element you want to bind the listener to. + * @param eventName - Name of the event you want listen for. + * @param handler - Function to fire when receiving the event. + * @param options - Options to pass to `Event.addEventListener`. + * + * @public + */ +function useDomEvent(ref, eventName, handler, options) { + React.useEffect(() => { + const element = ref.current; + if (handler && element) { + return create.addDomEvent(element, eventName, handler, options); + } + }, [ref, eventName, handler, options]); +} + +/** + * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ +class DragControls { + constructor() { + this.componentControls = new Set(); + } + /** + * Subscribe a component's internal `VisualElementDragControls` to the user-facing API. + * + * @internal + */ + subscribe(controls) { + this.componentControls.add(controls); + return () => this.componentControls.delete(controls); + } + /** + * Start a drag gesture on every `motion` component that has this set of drag controls + * passed into it via the `dragControls` prop. + * + * ```jsx + * dragControls.start(e, { + * snapToCursor: true + * }) + * ``` + * + * @param event - PointerEvent + * @param options - Options + * + * @public + */ + start(event, options) { + this.componentControls.forEach((controls) => { + controls.start(event.nativeEvent || event, options); + }); + } + /** + * Cancels a drag gesture. + * + * ```jsx + * dragControls.cancel() + * ``` + * + * @public + */ + cancel() { + this.componentControls.forEach((controls) => { + controls.cancel(); + }); + } + /** + * Stops a drag gesture. + * + * ```jsx + * dragControls.stop() + * ``` + * + * @public + */ + stop() { + this.componentControls.forEach((controls) => { + controls.stop(); + }); + } +} +const createDragControls = () => new DragControls(); +/** + * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop + * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we + * might want to initiate that dragging from a different component than the draggable one. + * + * By creating a `dragControls` using the `useDragControls` hook, we can pass this into + * the draggable component's `dragControls` prop. It exposes a `start` method + * that can start dragging from pointer events on other components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ +function useDragControls() { + return create.useConstant(createDragControls); +} + +/** + * Checks if a component is a `motion` component. + */ +function isMotionComponent(component) { + return (component !== null && + typeof component === "object" && + create.motionComponentSymbol in component); +} + +/** + * Unwraps a `motion` component and returns either a string for `motion.div` or + * the React component for `motion(Component)`. + * + * If the component is not a `motion` component it returns undefined. + */ +function unwrapMotionComponent(component) { + if (isMotionComponent(component)) { + return component[create.motionComponentSymbol]; + } + return undefined; +} + +function useInstantLayoutTransition() { + return startTransition; +} +function startTransition(callback) { + if (!create.rootProjectionNode.current) + return; + create.rootProjectionNode.current.isUpdating = false; + create.rootProjectionNode.current.blockUpdate(); + callback && callback(); +} + +function useResetProjection() { + const reset = React.useCallback(() => { + const root = create.rootProjectionNode.current; + if (!root) + return; + root.resetTree(); + }, []); + return reset; +} + +/** + * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments. + * + * An index value can be passed to the returned `cycle` function to cycle to a specific index. + * + * ```jsx + * import * as React from "react" + * import { motion, useCycle } from "framer-motion" + * + * export const MyComponent = () => { + * const [x, cycleX] = useCycle(0, 50, 100) + * + * return ( + * cycleX()} + * /> + * ) + * } + * ``` + * + * @param items - items to cycle through + * @returns [currentState, cycleState] + * + * @public + */ +function useCycle(...items) { + const index = React.useRef(0); + const [item, setItem] = React.useState(items[index.current]); + const runCycle = React.useCallback((next) => { + index.current = + typeof next !== "number" + ? motionUtils.wrap(0, items.length, index.current + 1) + : next; + setItem(items[index.current]); + }, + // The array will change on each call, but by putting items.length at + // the front of this array, we guarantee the dependency comparison will match up + // eslint-disable-next-line react-hooks/exhaustive-deps + [items.length, ...items]); + return [item, runCycle]; +} + +function useInView(ref, { root, margin, amount, once = false, initial = false, } = {}) { + const [isInView, setInView] = React.useState(initial); + React.useEffect(() => { + if (!ref.current || (once && isInView)) + return; + const onEnter = () => { + setInView(true); + return once ? undefined : () => setInView(false); + }; + const options = { + root: (root && root.current) || undefined, + margin, + amount, + }; + return inView(ref.current, onEnter, options); + }, [root, ref, margin, once, amount]); + return isInView; +} + +function useInstantTransition() { + const [forceUpdate, forcedRenderCount] = useForceUpdate(); + const startInstantLayoutTransition = useInstantLayoutTransition(); + const unlockOnFrameRef = React.useRef(-1); + React.useEffect(() => { + /** + * Unblock after two animation frames, otherwise this will unblock too soon. + */ + motionDom.frame.postRender(() => motionDom.frame.postRender(() => { + /** + * If the callback has been called again after the effect + * triggered this 2 frame delay, don't unblock animations. This + * prevents the previous effect from unblocking the current + * instant transition too soon. This becomes more likely when + * used in conjunction with React.startTransition(). + */ + if (forcedRenderCount !== unlockOnFrameRef.current) + return; + motionUtils.MotionGlobalConfig.instantAnimations = false; + })); + }, [forcedRenderCount]); + return (callback) => { + startInstantLayoutTransition(() => { + motionUtils.MotionGlobalConfig.instantAnimations = true; + forceUpdate(); + callback(); + unlockOnFrameRef.current = forcedRenderCount + 1; + }); + }; +} +function disableInstantTransitions() { + motionUtils.MotionGlobalConfig.instantAnimations = false; +} + +function usePageInView() { + const [isInView, setIsInView] = React.useState(true); + React.useEffect(() => { + const handleVisibilityChange = () => setIsInView(!document.hidden); + if (document.hidden) { + handleVisibilityChange(); + } + document.addEventListener("visibilitychange", handleVisibilityChange); + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, []); + return isInView; +} + +const appearAnimationStore = new Map(); +const appearComplete = new Map(); + +const appearStoreId = (elementId, valueName) => { + const key = motionDom.transformProps.has(valueName) ? "transform" : valueName; + return `${elementId}: ${key}`; +}; + +function handoffOptimizedAppearAnimation(elementId, valueName, frame) { + const storeId = appearStoreId(elementId, valueName); + const optimisedAnimation = appearAnimationStore.get(storeId); + if (!optimisedAnimation) { + return null; + } + const { animation, startTime } = optimisedAnimation; + function cancelAnimation() { + window.MotionCancelOptimisedAnimation?.(elementId, valueName, frame); + } + /** + * We can cancel the animation once it's finished now that we've synced + * with Motion. + * + * Prefer onfinish over finished as onfinish is backwards compatible with + * older browsers. + */ + animation.onfinish = cancelAnimation; + if (startTime === null || window.MotionHandoffIsComplete?.(elementId)) { + /** + * If the startTime is null, this animation is the Paint Ready detection animation + * and we can cancel it immediately without handoff. + * + * Or if we've already handed off the animation then we're now interrupting it. + * In which case we need to cancel it. + */ + cancelAnimation(); + return null; + } + else { + return startTime; + } +} + +/** + * A single time to use across all animations to manually set startTime + * and ensure they're all in sync. + */ +let startFrameTime; +/** + * A dummy animation to detect when Chrome is ready to start + * painting the page and hold off from triggering the real animation + * until then. We only need one animation to detect paint ready. + * + * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850 + */ +let readyAnimation; +/** + * Keep track of animations that were suspended vs cancelled so we + * can easily resume them when we're done measuring layout. + */ +const suspendedAnimations = new Set(); +function resumeSuspendedAnimations() { + suspendedAnimations.forEach((data) => { + data.animation.play(); + data.animation.startTime = data.startTime; + }); + suspendedAnimations.clear(); +} +function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) { + // Prevent optimised appear animations if Motion has already started animating. + if (window.MotionIsMounted) { + return; + } + const id = element.dataset[create.optimizedAppearDataId]; + if (!id) + return; + window.MotionHandoffAnimation = handoffOptimizedAppearAnimation; + const storeId = appearStoreId(id, name); + if (!readyAnimation) { + readyAnimation = motionDom.startWaapiAnimation(element, name, [keyframes[0], keyframes[0]], + /** + * 10 secs is basically just a super-safe duration to give Chrome + * long enough to get the animation ready. + */ + { duration: 10000, ease: "linear" }); + appearAnimationStore.set(storeId, { + animation: readyAnimation, + startTime: null, + }); + /** + * If there's no readyAnimation then there's been no instantiation + * of handoff animations. + */ + window.MotionHandoffAnimation = handoffOptimizedAppearAnimation; + window.MotionHasOptimisedAnimation = (elementId, valueName) => { + if (!elementId) + return false; + /** + * Keep a map of elementIds that have started animating. We check + * via ID instead of Element because of hydration errors and + * pre-hydration checks. We also actively record IDs as they start + * animating rather than simply checking for data-appear-id as + * this attrbute might be present but not lead to an animation, for + * instance if the element's appear animation is on a different + * breakpoint. + */ + if (!valueName) { + return appearComplete.has(elementId); + } + const animationId = appearStoreId(elementId, valueName); + return Boolean(appearAnimationStore.get(animationId)); + }; + window.MotionHandoffMarkAsComplete = (elementId) => { + if (appearComplete.has(elementId)) { + appearComplete.set(elementId, true); + } + }; + window.MotionHandoffIsComplete = (elementId) => { + return appearComplete.get(elementId) === true; + }; + /** + * We only need to cancel transform animations as + * they're the ones that will interfere with the + * layout animation measurements. + */ + window.MotionCancelOptimisedAnimation = (elementId, valueName, frame, canResume) => { + const animationId = appearStoreId(elementId, valueName); + const data = appearAnimationStore.get(animationId); + if (!data) + return; + if (frame && canResume === undefined) { + /** + * Wait until the end of the subsequent frame to cancel the animation + * to ensure we don't remove the animation before the main thread has + * had a chance to resolve keyframes and render. + */ + frame.postRender(() => { + frame.postRender(() => { + data.animation.cancel(); + }); + }); + } + else { + data.animation.cancel(); + } + if (frame && canResume) { + suspendedAnimations.add(data); + frame.render(resumeSuspendedAnimations); + } + else { + appearAnimationStore.delete(animationId); + /** + * If there are no more animations left, we can remove the cancel function. + * This will let us know when we can stop checking for conflicting layout animations. + */ + if (!appearAnimationStore.size) { + window.MotionCancelOptimisedAnimation = undefined; + } + } + }; + window.MotionCheckAppearSync = (visualElement, valueName, value) => { + const appearId = create.getOptimisedAppearId(visualElement); + if (!appearId) + return; + const valueIsOptimised = window.MotionHasOptimisedAnimation?.(appearId, valueName); + const externalAnimationValue = visualElement.props.values?.[valueName]; + if (!valueIsOptimised || !externalAnimationValue) + return; + const removeSyncCheck = value.on("change", (latestValue) => { + if (externalAnimationValue.get() !== latestValue) { + window.MotionCancelOptimisedAnimation?.(appearId, valueName); + removeSyncCheck(); + } + }); + return removeSyncCheck; + }; + } + const startAnimation = () => { + readyAnimation.cancel(); + const appearAnimation = motionDom.startWaapiAnimation(element, name, keyframes, options); + /** + * Record the time of the first started animation. We call performance.now() once + * here and once in handoff to ensure we're getting + * close to a frame-locked time. This keeps all animations in sync. + */ + if (startFrameTime === undefined) { + startFrameTime = performance.now(); + } + appearAnimation.startTime = startFrameTime; + appearAnimationStore.set(storeId, { + animation: appearAnimation, + startTime: startFrameTime, + }); + if (onReady) + onReady(appearAnimation); + }; + appearComplete.set(id, false); + if (readyAnimation.ready) { + readyAnimation.ready.then(startAnimation).catch(motionUtils.noop); + } + else { + startAnimation(); + } +} + +const createObject = () => ({}); +class StateVisualElement extends create.VisualElement { + constructor() { + super(...arguments); + this.measureInstanceViewportBox = create.createBox; + } + build() { } + resetTransform() { } + restoreTransform() { } + removeValueFromRenderState() { } + renderInstance() { } + scrapeMotionValuesFromProps() { + return createObject(); + } + getBaseTargetFromProps() { + return undefined; + } + readValueFromInstance(_state, key, options) { + return options.initialState[key] || 0; + } + sortInstanceNodePosition() { + return 0; + } +} +const useVisualState = create.makeUseVisualState({ + scrapeMotionValuesFromProps: createObject, + createRenderState: createObject, +}); +/** + * This is not an officially supported API and may be removed + * on any version. + */ +function useAnimatedState(initialState) { + const [animationState, setAnimationState] = React.useState(initialState); + const visualState = useVisualState({}, false); + const element = create.useConstant(() => { + return new StateVisualElement({ + props: { + onUpdate: (v) => { + setAnimationState({ ...v }); + }, + }, + visualState, + presenceContext: null, + }, { initialState }); + }); + React.useLayoutEffect(() => { + element.mount({}); + return () => element.unmount(); + }, [element]); + const startAnimation = create.useConstant(() => (animationDefinition) => { + return create.animateVisualElement(element, animationDefinition); + }); + return [animationState, startAnimation]; +} + +let id = 0; +const AnimateSharedLayout = ({ children }) => { + React__namespace.useEffect(() => { + motionUtils.invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations"); + }, []); + return (jsxRuntime.jsx(LayoutGroup, { id: create.useConstant(() => `asl-${id++}`), children: children })); +}; + +// Keep things reasonable and avoid scale: Infinity. In practise we might need +// to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1] +// to simply hide content at unreasonable scales. +const maxScale = 100000; +const invertScale = (scale) => scale > 0.001 ? 1 / scale : maxScale; +let hasWarned = false; +/** + * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse + * of their respective parent scales. + * + * This is useful for undoing the distortion of content when scaling a parent component. + * + * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent. + * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output + * of those instead. + * + * ```jsx + * const MyComponent = () => { + * const { scaleX, scaleY } = useInvertedScale() + * return + * } + * ``` + * + * @deprecated + */ +function useInvertedScale(scale) { + let parentScaleX = useMotionValue(1); + let parentScaleY = useMotionValue(1); + const { visualElement } = React.useContext(create.MotionContext); + motionUtils.invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component."); + motionUtils.warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead."); + hasWarned = true; + if (scale) { + parentScaleX = scale.scaleX || parentScaleX; + parentScaleY = scale.scaleY || parentScaleY; + } + else if (visualElement) { + parentScaleX = visualElement.getValue("scaleX", 1); + parentScaleY = visualElement.getValue("scaleY", 1); + } + const scaleX = useTransform(parentScaleX, invertScale); + const scaleY = useTransform(parentScaleY, invertScale); + return { scaleX, scaleY }; +} + +exports.FlatTree = create.FlatTree; +exports.LayoutGroupContext = create.LayoutGroupContext; +exports.MotionConfigContext = create.MotionConfigContext; +exports.MotionContext = create.MotionContext; +exports.PresenceContext = create.PresenceContext; +exports.SwitchLayoutGroupContext = create.SwitchLayoutGroupContext; +exports.VisualElement = create.VisualElement; +exports.addPointerEvent = create.addPointerEvent; +exports.addPointerInfo = create.addPointerInfo; +exports.addScaleCorrector = create.addScaleCorrector; +exports.animateVisualElement = create.animateVisualElement; +exports.animations = create.animations; +exports.buildTransform = create.buildTransform; +exports.calcLength = create.calcLength; +exports.createBox = create.createBox; +exports.createRendererMotionComponent = create.createRendererMotionComponent; +exports.delay = create.delay; +exports.distance = create.distance; +exports.distance2D = create.distance2D; +exports.filterProps = create.filterProps; +exports.isBrowser = create.isBrowser; +exports.isValidMotionProp = create.isValidMotionProp; +exports.makeUseVisualState = create.makeUseVisualState; +exports.optimizedAppearDataAttribute = create.optimizedAppearDataAttribute; +exports.resolveMotionValue = create.resolveMotionValue; +exports.useIsPresent = create.useIsPresent; +exports.useIsomorphicLayoutEffect = create.useIsomorphicLayoutEffect; +exports.usePresence = create.usePresence; +exports.visualElementStore = create.visualElementStore; +Object.defineProperty(exports, "MotionGlobalConfig", { + enumerable: true, + get: function () { return motionUtils.MotionGlobalConfig; } +}); +exports.AnimatePresence = AnimatePresence; +exports.AnimateSharedLayout = AnimateSharedLayout; +exports.DeprecatedLayoutGroupContext = DeprecatedLayoutGroupContext; +exports.DragControls = DragControls; +exports.LayoutGroup = LayoutGroup; +exports.LazyMotion = LazyMotion; +exports.MotionConfig = MotionConfig; +exports.Reorder = namespace; +exports.WillChangeMotionValue = WillChangeMotionValue; +exports.animate = animate; +exports.animateMini = animateMini; +exports.animationControls = animationControls; +exports.createScopedAnimate = createScopedAnimate; +exports.disableInstantTransitions = disableInstantTransitions; +exports.domAnimation = domAnimation; +exports.domMax = domMax; +exports.domMin = domMin; +exports.inView = inView; +exports.isMotionComponent = isMotionComponent; +exports.m = m; +exports.motion = motion; +exports.scroll = scroll; +exports.scrollInfo = scrollInfo; +exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation; +exports.unwrapMotionComponent = unwrapMotionComponent; +exports.useAnimate = useAnimate; +exports.useAnimateMini = useAnimateMini; +exports.useAnimation = useAnimation; +exports.useAnimationControls = useAnimationControls; +exports.useAnimationFrame = useAnimationFrame; +exports.useCycle = useCycle; +exports.useDeprecatedAnimatedState = useAnimatedState; +exports.useDeprecatedInvertedScale = useInvertedScale; +exports.useDomEvent = useDomEvent; +exports.useDragControls = useDragControls; +exports.useElementScroll = useElementScroll; +exports.useForceUpdate = useForceUpdate; +exports.useInView = useInView; +exports.useInstantLayoutTransition = useInstantLayoutTransition; +exports.useInstantTransition = useInstantTransition; +exports.useMotionTemplate = useMotionTemplate; +exports.useMotionValue = useMotionValue; +exports.useMotionValueEvent = useMotionValueEvent; +exports.usePageInView = usePageInView; +exports.usePresenceData = usePresenceData; +exports.useReducedMotion = useReducedMotion; +exports.useReducedMotionConfig = useReducedMotionConfig; +exports.useResetProjection = useResetProjection; +exports.useScroll = useScroll; +exports.useSpring = useSpring; +exports.useTime = useTime; +exports.useTransform = useTransform; +exports.useUnmountEffect = useUnmountEffect; +exports.useVelocity = useVelocity; +exports.useViewportScroll = useViewportScroll; +exports.useWillChange = useWillChange; +Object.keys(motionDom).forEach(function (k) { + if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { + enumerable: true, + get: function () { return motionDom[k]; } + }); +}); +Object.keys(motionUtils).forEach(function (k) { + if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { + enumerable: true, + get: function () { return motionUtils[k]; } + }); +}); diff --git a/node_modules/framer-motion/dist/cjs/m.js b/node_modules/framer-motion/dist/cjs/m.js new file mode 100644 index 00000000..314feb68 --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/m.js @@ -0,0 +1,1444 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var jsxRuntime = require('react/jsx-runtime'); +var motionUtils = require('motion-utils'); +var react = require('react'); +var motionDom = require('motion-dom'); + +const LayoutGroupContext = react.createContext({}); + +const LazyContext = react.createContext({ strict: false }); + +/** + * @public + */ +const MotionConfigContext = react.createContext({ + transformPagePoint: (p) => p, + isStatic: false, + reducedMotion: "never", +}); + +const MotionContext = /* @__PURE__ */ react.createContext({}); + +function isAnimationControls(v) { + return (v !== null && + typeof v === "object" && + typeof v.start === "function"); +} + +/** + * Decides if the supplied variable is variant label + */ +function isVariantLabel(v) { + return typeof v === "string" || Array.isArray(v); +} + +const variantPriorityOrder = [ + "animate", + "whileInView", + "whileFocus", + "whileHover", + "whileTap", + "whileDrag", + "exit", +]; +const variantProps = ["initial", ...variantPriorityOrder]; + +function isControllingVariants(props) { + return (isAnimationControls(props.animate) || + variantProps.some((name) => isVariantLabel(props[name]))); +} +function isVariantNode(props) { + return Boolean(isControllingVariants(props) || props.variants); +} + +function getCurrentTreeVariants(props, context) { + if (isControllingVariants(props)) { + const { initial, animate } = props; + return { + initial: initial === false || isVariantLabel(initial) + ? initial + : undefined, + animate: isVariantLabel(animate) ? animate : undefined, + }; + } + return props.inherit !== false ? context : {}; +} + +function useCreateMotionContext(props) { + const { initial, animate } = getCurrentTreeVariants(props, react.useContext(MotionContext)); + return react.useMemo(() => ({ initial, animate }), [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]); +} +function variantLabelsAsDependency(prop) { + return Array.isArray(prop) ? prop.join(" ") : prop; +} + +const isBrowser = typeof window !== "undefined"; + +const featureProps = { + animation: [ + "animate", + "variants", + "whileHover", + "whileTap", + "exit", + "whileInView", + "whileFocus", + "whileDrag", + ], + exit: ["exit"], + drag: ["drag", "dragControls"], + focus: ["whileFocus"], + hover: ["whileHover", "onHoverStart", "onHoverEnd"], + tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"], + pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"], + inView: ["whileInView", "onViewportEnter", "onViewportLeave"], + layout: ["layout", "layoutId"], +}; +const featureDefinitions = {}; +for (const key in featureProps) { + featureDefinitions[key] = { + isEnabled: (props) => featureProps[key].some((name) => !!props[name]), + }; +} + +function loadFeatures(features) { + for (const key in features) { + featureDefinitions[key] = { + ...featureDefinitions[key], + ...features[key], + }; + } +} + +const motionComponentSymbol = Symbol.for("motionComponentSymbol"); + +function isRefObject(ref) { + return (ref && + typeof ref === "object" && + Object.prototype.hasOwnProperty.call(ref, "current")); +} + +/** + * Creates a ref function that, when called, hydrates the provided + * external ref and VisualElement. + */ +function useMotionRef(visualState, visualElement, externalRef) { + return react.useCallback((instance) => { + if (instance) { + visualState.onMount && visualState.onMount(instance); + } + if (visualElement) { + if (instance) { + visualElement.mount(instance); + } + else { + visualElement.unmount(); + } + } + if (externalRef) { + if (typeof externalRef === "function") { + externalRef(instance); + } + else if (isRefObject(externalRef)) { + externalRef.current = instance; + } + } + }, + /** + * Only pass a new ref callback to React if we've received a visual element + * factory. Otherwise we'll be mounting/remounting every time externalRef + * or other dependencies change. + */ + [visualElement]); +} + +/** + * Convert camelCase to dash-case properties. + */ +const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase(); + +const optimizedAppearDataId = "framerAppearId"; +const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId); + +/** + * @public + */ +const PresenceContext = +/* @__PURE__ */ react.createContext(null); + +/** + * Internal, exported only for usage in Framer + */ +const SwitchLayoutGroupContext = react.createContext({}); + +const useIsomorphicLayoutEffect = isBrowser ? react.useLayoutEffect : react.useEffect; + +function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor) { + const { visualElement: parent } = react.useContext(MotionContext); + const lazyContext = react.useContext(LazyContext); + const presenceContext = react.useContext(PresenceContext); + const reducedMotionConfig = react.useContext(MotionConfigContext).reducedMotion; + const visualElementRef = react.useRef(null); + /** + * If we haven't preloaded a renderer, check to see if we have one lazy-loaded + */ + createVisualElement = createVisualElement || lazyContext.renderer; + if (!visualElementRef.current && createVisualElement) { + visualElementRef.current = createVisualElement(Component, { + visualState, + parent, + props, + presenceContext, + blockInitialAnimation: presenceContext + ? presenceContext.initial === false + : false, + reducedMotionConfig, + }); + } + const visualElement = visualElementRef.current; + /** + * Load Motion gesture and animation features. These are rendered as renderless + * components so each feature can optionally make use of React lifecycle methods. + */ + const initialLayoutGroupConfig = react.useContext(SwitchLayoutGroupContext); + if (visualElement && + !visualElement.projection && + ProjectionNodeConstructor && + (visualElement.type === "html" || visualElement.type === "svg")) { + createProjectionNode(visualElementRef.current, props, ProjectionNodeConstructor, initialLayoutGroupConfig); + } + const isMounted = react.useRef(false); + react.useInsertionEffect(() => { + /** + * Check the component has already mounted before calling + * `update` unnecessarily. This ensures we skip the initial update. + */ + if (visualElement && isMounted.current) { + visualElement.update(props, presenceContext); + } + }); + /** + * Cache this value as we want to know whether HandoffAppearAnimations + * was present on initial render - it will be deleted after this. + */ + const optimisedAppearId = props[optimizedAppearDataAttribute]; + const wantsHandoff = react.useRef(Boolean(optimisedAppearId) && + !window.MotionHandoffIsComplete?.(optimisedAppearId) && + window.MotionHasOptimisedAnimation?.(optimisedAppearId)); + useIsomorphicLayoutEffect(() => { + if (!visualElement) + return; + isMounted.current = true; + window.MotionIsMounted = true; + visualElement.updateFeatures(); + motionDom.microtask.render(visualElement.render); + /** + * Ideally this function would always run in a useEffect. + * + * However, if we have optimised appear animations to handoff from, + * it needs to happen synchronously to ensure there's no flash of + * incorrect styles in the event of a hydration error. + * + * So if we detect a situtation where optimised appear animations + * are running, we use useLayoutEffect to trigger animations. + */ + if (wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + }); + react.useEffect(() => { + if (!visualElement) + return; + if (!wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + if (wantsHandoff.current) { + // This ensures all future calls to animateChanges() in this component will run in useEffect + queueMicrotask(() => { + window.MotionHandoffMarkAsComplete?.(optimisedAppearId); + }); + wantsHandoff.current = false; + } + }); + return visualElement; +} +function createProjectionNode(visualElement, props, ProjectionNodeConstructor, initialPromotionConfig) { + const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, layoutCrossfade, } = props; + visualElement.projection = new ProjectionNodeConstructor(visualElement.latestValues, props["data-framer-portal-id"] + ? undefined + : getClosestProjectingNode(visualElement.parent)); + visualElement.projection.setOptions({ + layoutId, + layout, + alwaysMeasureLayout: Boolean(drag) || (dragConstraints && isRefObject(dragConstraints)), + visualElement, + /** + * TODO: Update options in an effect. This could be tricky as it'll be too late + * to update by the time layout animations run. + * We also need to fix this safeToRemove by linking it up to the one returned by usePresence, + * ensuring it gets called if there's no potential layout animations. + * + */ + animationType: typeof layout === "string" ? layout : "both", + initialPromotionConfig, + crossfade: layoutCrossfade, + layoutScroll, + layoutRoot, + }); +} +function getClosestProjectingNode(visualElement) { + if (!visualElement) + return undefined; + return visualElement.options.allowProjection !== false + ? visualElement.projection + : getClosestProjectingNode(visualElement.parent); +} + +/** + * Create a `motion` component. + * + * This function accepts a Component argument, which can be either a string (ie "div" + * for `motion.div`), or an actual React component. + * + * Alongside this is a config option which provides a way of rendering the provided + * component "offline", or outside the React render cycle. + */ +function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) { + preloadedFeatures && loadFeatures(preloadedFeatures); + function MotionComponent(props, externalRef) { + /** + * If we need to measure the element we load this functionality in a + * separate class component in order to gain access to getSnapshotBeforeUpdate. + */ + let MeasureLayout; + const configAndProps = { + ...react.useContext(MotionConfigContext), + ...props, + layoutId: useLayoutId(props), + }; + const { isStatic } = configAndProps; + const context = useCreateMotionContext(props); + const visualState = useVisualState(props, isStatic); + if (!isStatic && isBrowser) { + useStrictMode(configAndProps, preloadedFeatures); + const layoutProjection = getProjectionFunctionality(configAndProps); + MeasureLayout = layoutProjection.MeasureLayout; + /** + * Create a VisualElement for this component. A VisualElement provides a common + * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as + * providing a way of rendering to these APIs outside of the React render loop + * for more performant animations and interactions + */ + context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode); + } + /** + * The mount order and hierarchy is specific to ensure our element ref + * is hydrated by the time features fire their effects. + */ + return (jsxRuntime.jsxs(MotionContext.Provider, { value: context, children: [MeasureLayout && context.visualElement ? (jsxRuntime.jsx(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null, useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)] })); + } + MotionComponent.displayName = `motion.${typeof Component === "string" + ? Component + : `create(${Component.displayName ?? Component.name ?? ""})`}`; + const ForwardRefMotionComponent = react.forwardRef(MotionComponent); + ForwardRefMotionComponent[motionComponentSymbol] = Component; + return ForwardRefMotionComponent; +} +function useLayoutId({ layoutId }) { + const layoutGroupId = react.useContext(LayoutGroupContext).id; + return layoutGroupId && layoutId !== undefined + ? layoutGroupId + "-" + layoutId + : layoutId; +} +function useStrictMode(configAndProps, preloadedFeatures) { + const isStrict = react.useContext(LazyContext).strict; + /** + * If we're in development mode, check to make sure we're not rendering a motion component + * as a child of LazyMotion, as this will break the file-size benefits of using it. + */ + if (process.env.NODE_ENV !== "production" && + preloadedFeatures && + isStrict) { + const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead."; + configAndProps.ignoreStrict + ? motionUtils.warning(false, strictMessage) + : motionUtils.invariant(false, strictMessage); + } +} +function getProjectionFunctionality(props) { + const { drag, layout } = featureDefinitions; + if (!drag && !layout) + return {}; + const combined = { ...drag, ...layout }; + return { + MeasureLayout: drag?.isEnabled(props) || layout?.isEnabled(props) + ? combined.MeasureLayout + : undefined, + ProjectionNode: combined.ProjectionNode, + }; +} + +const scaleCorrectors = {}; + +function isForcedMotionValue(key, { layout, layoutId }) { + return (motionDom.transformProps.has(key) || + key.startsWith("origin") || + ((layout || layoutId !== undefined) && + (!!scaleCorrectors[key] || key === "opacity"))); +} + +const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", +}; +const numTransforms = motionDom.transformPropOrder.length; +/** + * Build a CSS transform style from individual x/y/scale etc properties. + * + * This outputs with a default order of transforms/scales/rotations, this can be customised by + * providing a transformTemplate function. + */ +function buildTransform(latestValues, transform, transformTemplate) { + // The transform string we're going to build into. + let transformString = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < numTransforms; i++) { + const key = motionDom.transformPropOrder[i]; + const value = latestValues[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault || transformTemplate) { + const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]); + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + transformString += `${transformName}(${valueAsType}) `; + } + if (transformTemplate) { + transform[key] = valueAsType; + } + } + } + transformString = transformString.trim(); + // If we have a custom `transform` template, pass our transform values and + // generated transformString to that before returning + if (transformTemplate) { + transformString = transformTemplate(transform, transformIsDefault ? "" : transformString); + } + else if (transformIsDefault) { + transformString = "none"; + } + return transformString; +} + +function buildHTMLStyles(state, latestValues, transformTemplate) { + const { style, vars, transformOrigin } = state; + // Track whether we encounter any transform or transformOrigin values. + let hasTransform = false; + let hasTransformOrigin = false; + /** + * Loop over all our latest animated values and decide whether to handle them + * as a style or CSS variable. + * + * Transforms and transform origins are kept separately for further processing. + */ + for (const key in latestValues) { + const value = latestValues[key]; + if (motionDom.transformProps.has(key)) { + // If this is a transform, flag to enable further transform processing + hasTransform = true; + continue; + } + else if (motionDom.isCSSVariableName(key)) { + vars[key] = value; + continue; + } + else { + // Convert the value to its default value type, ie 0 -> "0px" + const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]); + if (key.startsWith("origin")) { + // If this is a transform origin, flag and enable further transform-origin processing + hasTransformOrigin = true; + transformOrigin[key] = + valueAsType; + } + else { + style[key] = valueAsType; + } + } + } + if (!latestValues.transform) { + if (hasTransform || transformTemplate) { + style.transform = buildTransform(latestValues, state.transform, transformTemplate); + } + else if (style.transform) { + /** + * If we have previously created a transform but currently don't have any, + * reset transform style to none. + */ + style.transform = "none"; + } + } + /** + * Build a transformOrigin style. Uses the same defaults as the browser for + * undefined origins. + */ + if (hasTransformOrigin) { + const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin; + style.transformOrigin = `${originX} ${originY} ${originZ}`; + } +} + +const createHtmlRenderState = () => ({ + style: {}, + transform: {}, + transformOrigin: {}, + vars: {}, +}); + +function copyRawValuesOnly(target, source, props) { + for (const key in source) { + if (!motionDom.isMotionValue(source[key]) && !isForcedMotionValue(key, props)) { + target[key] = source[key]; + } + } +} +function useInitialMotionValues({ transformTemplate }, visualState) { + return react.useMemo(() => { + const state = createHtmlRenderState(); + buildHTMLStyles(state, visualState, transformTemplate); + return Object.assign({}, state.vars, state.style); + }, [visualState]); +} +function useStyle(props, visualState) { + const styleProp = props.style || {}; + const style = {}; + /** + * Copy non-Motion Values straight into style + */ + copyRawValuesOnly(style, styleProp, props); + Object.assign(style, useInitialMotionValues(props, visualState)); + return style; +} +function useHTMLProps(props, visualState) { + // The `any` isn't ideal but it is the type of createElement props argument + const htmlProps = {}; + const style = useStyle(props, visualState); + if (props.drag && props.dragListener !== false) { + // Disable the ghost element when a user drags + htmlProps.draggable = false; + // Disable text selection + style.userSelect = + style.WebkitUserSelect = + style.WebkitTouchCallout = + "none"; + // Disable scrolling on the draggable direction + style.touchAction = + props.drag === true + ? "none" + : `pan-${props.drag === "x" ? "y" : "x"}`; + } + if (props.tabIndex === undefined && + (props.onTap || props.onTapStart || props.whileTap)) { + htmlProps.tabIndex = 0; + } + htmlProps.style = style; + return htmlProps; +} + +const dashKeys = { + offset: "stroke-dashoffset", + array: "stroke-dasharray", +}; +const camelKeys = { + offset: "strokeDashoffset", + array: "strokeDasharray", +}; +/** + * Build SVG path properties. Uses the path's measured length to convert + * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset + * and stroke-dasharray attributes. + * + * This function is mutative to reduce per-frame GC. + */ +function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) { + // Normalise path length by setting SVG attribute pathLength to 1 + attrs.pathLength = 1; + // We use dash case when setting attributes directly to the DOM node and camel case + // when defining props on a React component. + const keys = useDashCase ? dashKeys : camelKeys; + // Build the dash offset + attrs[keys.offset] = motionDom.px.transform(-offset); + // Build the dash array + const pathLength = motionDom.px.transform(length); + const pathSpacing = motionDom.px.transform(spacing); + attrs[keys.array] = `${pathLength} ${pathSpacing}`; +} + +/** + * Build SVG visual attributes, like cx and style.transform + */ +function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0, +// This is object creation, which we try to avoid per-frame. +...latest }, isSVGTag, transformTemplate, styleProp) { + buildHTMLStyles(state, latest, transformTemplate); + /** + * For svg tags we just want to make sure viewBox is animatable and treat all the styles + * as normal HTML tags. + */ + if (isSVGTag) { + if (state.style.viewBox) { + state.attrs.viewBox = state.style.viewBox; + } + return; + } + state.attrs = state.style; + state.style = {}; + const { attrs, style } = state; + /** + * However, we apply transforms as CSS transforms. + * So if we detect a transform, transformOrigin we take it from attrs and copy it into style. + */ + if (attrs.transform) { + style.transform = attrs.transform; + delete attrs.transform; + } + if (style.transform || attrs.transformOrigin) { + style.transformOrigin = attrs.transformOrigin ?? "50% 50%"; + delete attrs.transformOrigin; + } + if (style.transform) { + /** + * SVG's element transform-origin uses its own median as a reference. + * Therefore, transformBox becomes a fill-box + */ + style.transformBox = styleProp?.transformBox ?? "fill-box"; + delete attrs.transformBox; + } + // Render attrX/attrY/attrScale as attributes + if (attrX !== undefined) + attrs.x = attrX; + if (attrY !== undefined) + attrs.y = attrY; + if (attrScale !== undefined) + attrs.scale = attrScale; + // Build SVG path if one has been defined + if (pathLength !== undefined) { + buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false); + } +} + +const createSvgRenderState = () => ({ + ...createHtmlRenderState(), + attrs: {}, +}); + +const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg"; + +function useSVGProps(props, visualState, _isStatic, Component) { + const visualProps = react.useMemo(() => { + const state = createSvgRenderState(); + buildSVGAttrs(state, visualState, isSVGTag(Component), props.transformTemplate, props.style); + return { + ...state.attrs, + style: { ...state.style }, + }; + }, [visualState]); + if (props.style) { + const rawStyles = {}; + copyRawValuesOnly(rawStyles, props.style, props); + visualProps.style = { ...rawStyles, ...visualProps.style }; + } + return visualProps; +} + +/** + * A list of all valid MotionProps. + * + * @privateRemarks + * This doesn't throw if a `MotionProp` name is missing - it should. + */ +const validMotionProps = new Set([ + "animate", + "exit", + "variants", + "initial", + "style", + "values", + "variants", + "transition", + "transformTemplate", + "custom", + "inherit", + "onBeforeLayoutMeasure", + "onAnimationStart", + "onAnimationComplete", + "onUpdate", + "onDragStart", + "onDrag", + "onDragEnd", + "onMeasureDragConstraints", + "onDirectionLock", + "onDragTransitionEnd", + "_dragX", + "_dragY", + "onHoverStart", + "onHoverEnd", + "onViewportEnter", + "onViewportLeave", + "globalTapTarget", + "ignoreStrict", + "viewport", +]); +/** + * Check whether a prop name is a valid `MotionProp` key. + * + * @param key - Name of the property to check + * @returns `true` is key is a valid `MotionProp`. + * + * @public + */ +function isValidMotionProp(key) { + return (key.startsWith("while") || + (key.startsWith("drag") && key !== "draggable") || + key.startsWith("layout") || + key.startsWith("onTap") || + key.startsWith("onPan") || + key.startsWith("onLayout") || + validMotionProps.has(key)); +} + +let shouldForward = (key) => !isValidMotionProp(key); +function loadExternalIsValidProp(isValidProp) { + if (typeof isValidProp !== "function") + return; + // Explicitly filter our events + shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key); +} +/** + * Emotion and Styled Components both allow users to pass through arbitrary props to their components + * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which + * of these should be passed to the underlying DOM node. + * + * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props + * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props + * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of + * `@emotion/is-prop-valid`, however to fix this problem we need to use it. + * + * By making it an optionalDependency we can offer this functionality only in the situations where it's + * actually required. + */ +try { + /** + * We attempt to import this package but require won't be defined in esm environments, in that case + * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed + * in favour of explicit injection. + */ + loadExternalIsValidProp(require("@emotion/is-prop-valid").default); +} +catch { + // We don't need to actually do anything here - the fallback is the existing `isPropValid`. +} +function filterProps(props, isDom, forwardMotionProps) { + const filteredProps = {}; + for (const key in props) { + /** + * values is considered a valid prop by Emotion, so if it's present + * this will be rendered out to the DOM unless explicitly filtered. + * + * We check the type as it could be used with the `feColorMatrix` + * element, which we support. + */ + if (key === "values" && typeof props.values === "object") + continue; + if (shouldForward(key) || + (forwardMotionProps === true && isValidMotionProp(key)) || + (!isDom && !isValidMotionProp(key)) || + // If trying to use native HTML drag events, forward drag listeners + (props["draggable"] && + key.startsWith("onDrag"))) { + filteredProps[key] = + props[key]; + } + } + return filteredProps; +} + +/** + * We keep these listed separately as we use the lowercase tag names as part + * of the runtime bundle to detect SVG components + */ +const lowercaseSVGElements = [ + "animate", + "circle", + "defs", + "desc", + "ellipse", + "g", + "image", + "line", + "filter", + "marker", + "mask", + "metadata", + "path", + "pattern", + "polygon", + "polyline", + "rect", + "stop", + "switch", + "symbol", + "svg", + "text", + "tspan", + "use", + "view", +]; + +function isSVGComponent(Component) { + if ( + /** + * If it's not a string, it's a custom React component. Currently we only support + * HTML custom React components. + */ + typeof Component !== "string" || + /** + * If it contains a dash, the element is a custom HTML webcomponent. + */ + Component.includes("-")) { + return false; + } + else if ( + /** + * If it's in our list of lowercase SVG tags, it's an SVG component + */ + lowercaseSVGElements.indexOf(Component) > -1 || + /** + * If it contains a capital letter, it's an SVG component + */ + /[A-Z]/u.test(Component)) { + return true; + } + return false; +} + +function createUseRender(forwardMotionProps = false) { + const useRender = (Component, props, ref, { latestValues }, isStatic) => { + const useVisualProps = isSVGComponent(Component) + ? useSVGProps + : useHTMLProps; + const visualProps = useVisualProps(props, latestValues, isStatic, Component); + const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps); + const elementProps = Component !== react.Fragment + ? { ...filteredProps, ...visualProps, ref } + : {}; + /** + * If component has been handed a motion value as its child, + * memoise its initial value and render that. Subsequent updates + * will be handled by the onChange handler + */ + const { children } = props; + const renderedChildren = react.useMemo(() => (motionDom.isMotionValue(children) ? children.get() : children), [children]); + return react.createElement(Component, { + ...elementProps, + children: renderedChildren, + }); + }; + return useRender; +} + +function getValueState(visualElement) { + const state = [{}, {}]; + visualElement?.values.forEach((value, key) => { + state[0][key] = value.get(); + state[1][key] = value.getVelocity(); + }); + return state; +} +function resolveVariantFromProps(props, definition, custom, visualElement) { + /** + * If the variant definition is a function, resolve. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + /** + * If the variant definition is a variant label, or + * the function returned a variant label, resolve. + */ + if (typeof definition === "string") { + definition = props.variants && props.variants[definition]; + } + /** + * At this point we've resolved both functions and variant labels, + * but the resolved variant label might itself have been a function. + * If so, resolve. This can only have returned a valid target object. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + return definition; +} + +/** + * Creates a constant value over the lifecycle of a component. + * + * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer + * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` + * you can ensure that initialisers don't execute twice or more. + */ +function useConstant(init) { + const ref = react.useRef(null); + if (ref.current === null) { + ref.current = init(); + } + return ref.current; +} + +/** + * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself + * + * TODO: Remove and move to library + */ +function resolveMotionValue(value) { + return motionDom.isMotionValue(value) ? value.get() : value; +} + +function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) { + const state = { + latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps), + renderState: createRenderState(), + }; + return state; +} +const makeUseVisualState = (config) => (props, isStatic) => { + const context = react.useContext(MotionContext); + const presenceContext = react.useContext(PresenceContext); + const make = () => makeState(config, props, context, presenceContext); + return isStatic ? make() : useConstant(make); +}; +function makeLatestValues(props, context, presenceContext, scrapeMotionValues) { + const values = {}; + const motionValues = scrapeMotionValues(props, {}); + for (const key in motionValues) { + values[key] = resolveMotionValue(motionValues[key]); + } + let { initial, animate } = props; + const isControllingVariants$1 = isControllingVariants(props); + const isVariantNode$1 = isVariantNode(props); + if (context && + isVariantNode$1 && + !isControllingVariants$1 && + props.inherit !== false) { + if (initial === undefined) + initial = context.initial; + if (animate === undefined) + animate = context.animate; + } + let isInitialAnimationBlocked = presenceContext + ? presenceContext.initial === false + : false; + isInitialAnimationBlocked = isInitialAnimationBlocked || initial === false; + const variantToSet = isInitialAnimationBlocked ? animate : initial; + if (variantToSet && + typeof variantToSet !== "boolean" && + !isAnimationControls(variantToSet)) { + const list = Array.isArray(variantToSet) ? variantToSet : [variantToSet]; + for (let i = 0; i < list.length; i++) { + const resolved = resolveVariantFromProps(props, list[i]); + if (resolved) { + const { transitionEnd, transition, ...target } = resolved; + for (const key in target) { + let valueTarget = target[key]; + if (Array.isArray(valueTarget)) { + /** + * Take final keyframe if the initial animation is blocked because + * we want to initialise at the end of that blocked animation. + */ + const index = isInitialAnimationBlocked + ? valueTarget.length - 1 + : 0; + valueTarget = valueTarget[index]; + } + if (valueTarget !== null) { + values[key] = valueTarget; + } + } + for (const key in transitionEnd) { + values[key] = transitionEnd[key]; + } + } + } + } + return values; +} + +function scrapeMotionValuesFromProps$1(props, prevProps, visualElement) { + const { style } = props; + const newValues = {}; + for (const key in style) { + if (motionDom.isMotionValue(style[key]) || + (prevProps.style && + motionDom.isMotionValue(prevProps.style[key])) || + isForcedMotionValue(key, props) || + visualElement?.getValue(key)?.liveStyle !== undefined) { + newValues[key] = style[key]; + } + } + return newValues; +} + +const htmlMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps$1, + createRenderState: createHtmlRenderState, + }), +}; + +function scrapeMotionValuesFromProps(props, prevProps, visualElement) { + const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + for (const key in props) { + if (motionDom.isMotionValue(props[key]) || + motionDom.isMotionValue(prevProps[key])) { + const targetKey = motionDom.transformPropOrder.indexOf(key) !== -1 + ? "attr" + key.charAt(0).toUpperCase() + key.substring(1) + : key; + newValues[targetKey] = props[key]; + } + } + return newValues; +} + +const svgMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps, + createRenderState: createSvgRenderState, + }), +}; + +function createMotionComponentFactory(preloadedFeatures, createVisualElement) { + return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) { + const baseConfig = isSVGComponent(Component) + ? svgMotionConfig + : htmlMotionConfig; + const config = { + ...baseConfig, + preloadedFeatures, + useRender: createUseRender(forwardMotionProps), + createVisualElement, + Component, + }; + return createRendererMotionComponent(config); + }; +} + +const createMinimalMotionComponent = +/*@__PURE__*/ createMotionComponentFactory(); + +/** + * HTML components + */ +const MotionA = /*@__PURE__*/ createMinimalMotionComponent("a"); +const MotionAbbr = /*@__PURE__*/ createMinimalMotionComponent("abbr"); +const MotionAddress = +/*@__PURE__*/ createMinimalMotionComponent("address"); +const MotionArea = /*@__PURE__*/ createMinimalMotionComponent("area"); +const MotionArticle = +/*@__PURE__*/ createMinimalMotionComponent("article"); +const MotionAside = /*@__PURE__*/ createMinimalMotionComponent("aside"); +const MotionAudio = /*@__PURE__*/ createMinimalMotionComponent("audio"); +const MotionB = /*@__PURE__*/ createMinimalMotionComponent("b"); +const MotionBase = /*@__PURE__*/ createMinimalMotionComponent("base"); +const MotionBdi = /*@__PURE__*/ createMinimalMotionComponent("bdi"); +const MotionBdo = /*@__PURE__*/ createMinimalMotionComponent("bdo"); +const MotionBig = /*@__PURE__*/ createMinimalMotionComponent("big"); +const MotionBlockquote = +/*@__PURE__*/ createMinimalMotionComponent("blockquote"); +const MotionBody = /*@__PURE__*/ createMinimalMotionComponent("body"); +const MotionButton = /*@__PURE__*/ createMinimalMotionComponent("button"); +const MotionCanvas = /*@__PURE__*/ createMinimalMotionComponent("canvas"); +const MotionCaption = +/*@__PURE__*/ createMinimalMotionComponent("caption"); +const MotionCite = /*@__PURE__*/ createMinimalMotionComponent("cite"); +const MotionCode = /*@__PURE__*/ createMinimalMotionComponent("code"); +const MotionCol = /*@__PURE__*/ createMinimalMotionComponent("col"); +const MotionColgroup = +/*@__PURE__*/ createMinimalMotionComponent("colgroup"); +const MotionData = /*@__PURE__*/ createMinimalMotionComponent("data"); +const MotionDatalist = +/*@__PURE__*/ createMinimalMotionComponent("datalist"); +const MotionDd = /*@__PURE__*/ createMinimalMotionComponent("dd"); +const MotionDel = /*@__PURE__*/ createMinimalMotionComponent("del"); +const MotionDetails = +/*@__PURE__*/ createMinimalMotionComponent("details"); +const MotionDfn = /*@__PURE__*/ createMinimalMotionComponent("dfn"); +const MotionDialog = /*@__PURE__*/ createMinimalMotionComponent("dialog"); +const MotionDiv = /*@__PURE__*/ createMinimalMotionComponent("div"); +const MotionDl = /*@__PURE__*/ createMinimalMotionComponent("dl"); +const MotionDt = /*@__PURE__*/ createMinimalMotionComponent("dt"); +const MotionEm = /*@__PURE__*/ createMinimalMotionComponent("em"); +const MotionEmbed = /*@__PURE__*/ createMinimalMotionComponent("embed"); +const MotionFieldset = +/*@__PURE__*/ createMinimalMotionComponent("fieldset"); +const MotionFigcaption = +/*@__PURE__*/ createMinimalMotionComponent("figcaption"); +const MotionFigure = /*@__PURE__*/ createMinimalMotionComponent("figure"); +const MotionFooter = /*@__PURE__*/ createMinimalMotionComponent("footer"); +const MotionForm = /*@__PURE__*/ createMinimalMotionComponent("form"); +const MotionH1 = /*@__PURE__*/ createMinimalMotionComponent("h1"); +const MotionH2 = /*@__PURE__*/ createMinimalMotionComponent("h2"); +const MotionH3 = /*@__PURE__*/ createMinimalMotionComponent("h3"); +const MotionH4 = /*@__PURE__*/ createMinimalMotionComponent("h4"); +const MotionH5 = /*@__PURE__*/ createMinimalMotionComponent("h5"); +const MotionH6 = /*@__PURE__*/ createMinimalMotionComponent("h6"); +const MotionHead = /*@__PURE__*/ createMinimalMotionComponent("head"); +const MotionHeader = /*@__PURE__*/ createMinimalMotionComponent("header"); +const MotionHgroup = /*@__PURE__*/ createMinimalMotionComponent("hgroup"); +const MotionHr = /*@__PURE__*/ createMinimalMotionComponent("hr"); +const MotionHtml = /*@__PURE__*/ createMinimalMotionComponent("html"); +const MotionI = /*@__PURE__*/ createMinimalMotionComponent("i"); +const MotionIframe = /*@__PURE__*/ createMinimalMotionComponent("iframe"); +const MotionImg = /*@__PURE__*/ createMinimalMotionComponent("img"); +const MotionInput = /*@__PURE__*/ createMinimalMotionComponent("input"); +const MotionIns = /*@__PURE__*/ createMinimalMotionComponent("ins"); +const MotionKbd = /*@__PURE__*/ createMinimalMotionComponent("kbd"); +const MotionKeygen = /*@__PURE__*/ createMinimalMotionComponent("keygen"); +const MotionLabel = /*@__PURE__*/ createMinimalMotionComponent("label"); +const MotionLegend = /*@__PURE__*/ createMinimalMotionComponent("legend"); +const MotionLi = /*@__PURE__*/ createMinimalMotionComponent("li"); +const MotionLink = /*@__PURE__*/ createMinimalMotionComponent("link"); +const MotionMain = /*@__PURE__*/ createMinimalMotionComponent("main"); +const MotionMap = /*@__PURE__*/ createMinimalMotionComponent("map"); +const MotionMark = /*@__PURE__*/ createMinimalMotionComponent("mark"); +const MotionMenu = /*@__PURE__*/ createMinimalMotionComponent("menu"); +const MotionMenuitem = +/*@__PURE__*/ createMinimalMotionComponent("menuitem"); +const MotionMeter = /*@__PURE__*/ createMinimalMotionComponent("meter"); +const MotionNav = /*@__PURE__*/ createMinimalMotionComponent("nav"); +const MotionObject = /*@__PURE__*/ createMinimalMotionComponent("object"); +const MotionOl = /*@__PURE__*/ createMinimalMotionComponent("ol"); +const MotionOptgroup = +/*@__PURE__*/ createMinimalMotionComponent("optgroup"); +const MotionOption = /*@__PURE__*/ createMinimalMotionComponent("option"); +const MotionOutput = /*@__PURE__*/ createMinimalMotionComponent("output"); +const MotionP = /*@__PURE__*/ createMinimalMotionComponent("p"); +const MotionParam = /*@__PURE__*/ createMinimalMotionComponent("param"); +const MotionPicture = +/*@__PURE__*/ createMinimalMotionComponent("picture"); +const MotionPre = /*@__PURE__*/ createMinimalMotionComponent("pre"); +const MotionProgress = +/*@__PURE__*/ createMinimalMotionComponent("progress"); +const MotionQ = /*@__PURE__*/ createMinimalMotionComponent("q"); +const MotionRp = /*@__PURE__*/ createMinimalMotionComponent("rp"); +const MotionRt = /*@__PURE__*/ createMinimalMotionComponent("rt"); +const MotionRuby = /*@__PURE__*/ createMinimalMotionComponent("ruby"); +const MotionS = /*@__PURE__*/ createMinimalMotionComponent("s"); +const MotionSamp = /*@__PURE__*/ createMinimalMotionComponent("samp"); +const MotionScript = /*@__PURE__*/ createMinimalMotionComponent("script"); +const MotionSection = +/*@__PURE__*/ createMinimalMotionComponent("section"); +const MotionSelect = /*@__PURE__*/ createMinimalMotionComponent("select"); +const MotionSmall = /*@__PURE__*/ createMinimalMotionComponent("small"); +const MotionSource = /*@__PURE__*/ createMinimalMotionComponent("source"); +const MotionSpan = /*@__PURE__*/ createMinimalMotionComponent("span"); +const MotionStrong = /*@__PURE__*/ createMinimalMotionComponent("strong"); +const MotionStyle = /*@__PURE__*/ createMinimalMotionComponent("style"); +const MotionSub = /*@__PURE__*/ createMinimalMotionComponent("sub"); +const MotionSummary = +/*@__PURE__*/ createMinimalMotionComponent("summary"); +const MotionSup = /*@__PURE__*/ createMinimalMotionComponent("sup"); +const MotionTable = /*@__PURE__*/ createMinimalMotionComponent("table"); +const MotionTbody = /*@__PURE__*/ createMinimalMotionComponent("tbody"); +const MotionTd = /*@__PURE__*/ createMinimalMotionComponent("td"); +const MotionTextarea = +/*@__PURE__*/ createMinimalMotionComponent("textarea"); +const MotionTfoot = /*@__PURE__*/ createMinimalMotionComponent("tfoot"); +const MotionTh = /*@__PURE__*/ createMinimalMotionComponent("th"); +const MotionThead = /*@__PURE__*/ createMinimalMotionComponent("thead"); +const MotionTime = /*@__PURE__*/ createMinimalMotionComponent("time"); +const MotionTitle = /*@__PURE__*/ createMinimalMotionComponent("title"); +const MotionTr = /*@__PURE__*/ createMinimalMotionComponent("tr"); +const MotionTrack = /*@__PURE__*/ createMinimalMotionComponent("track"); +const MotionU = /*@__PURE__*/ createMinimalMotionComponent("u"); +const MotionUl = /*@__PURE__*/ createMinimalMotionComponent("ul"); +const MotionVideo = /*@__PURE__*/ createMinimalMotionComponent("video"); +const MotionWbr = /*@__PURE__*/ createMinimalMotionComponent("wbr"); +const MotionWebview = +/*@__PURE__*/ createMinimalMotionComponent("webview"); +/** + * SVG components + */ +const MotionAnimate = +/*@__PURE__*/ createMinimalMotionComponent("animate"); +const MotionCircle = /*@__PURE__*/ createMinimalMotionComponent("circle"); +const MotionDefs = /*@__PURE__*/ createMinimalMotionComponent("defs"); +const MotionDesc = /*@__PURE__*/ createMinimalMotionComponent("desc"); +const MotionEllipse = +/*@__PURE__*/ createMinimalMotionComponent("ellipse"); +const MotionG = /*@__PURE__*/ createMinimalMotionComponent("g"); +const MotionImage = /*@__PURE__*/ createMinimalMotionComponent("image"); +const MotionLine = /*@__PURE__*/ createMinimalMotionComponent("line"); +const MotionFilter = /*@__PURE__*/ createMinimalMotionComponent("filter"); +const MotionMarker = /*@__PURE__*/ createMinimalMotionComponent("marker"); +const MotionMask = /*@__PURE__*/ createMinimalMotionComponent("mask"); +const MotionMetadata = +/*@__PURE__*/ createMinimalMotionComponent("metadata"); +const MotionPath = /*@__PURE__*/ createMinimalMotionComponent("path"); +const MotionPattern = +/*@__PURE__*/ createMinimalMotionComponent("pattern"); +const MotionPolygon = +/*@__PURE__*/ createMinimalMotionComponent("polygon"); +const MotionPolyline = +/*@__PURE__*/ createMinimalMotionComponent("polyline"); +const MotionRect = /*@__PURE__*/ createMinimalMotionComponent("rect"); +const MotionStop = /*@__PURE__*/ createMinimalMotionComponent("stop"); +const MotionSvg = /*@__PURE__*/ createMinimalMotionComponent("svg"); +const MotionSymbol = /*@__PURE__*/ createMinimalMotionComponent("symbol"); +const MotionText = /*@__PURE__*/ createMinimalMotionComponent("text"); +const MotionTspan = /*@__PURE__*/ createMinimalMotionComponent("tspan"); +const MotionUse = /*@__PURE__*/ createMinimalMotionComponent("use"); +const MotionView = /*@__PURE__*/ createMinimalMotionComponent("view"); +const MotionClipPath = +/*@__PURE__*/ createMinimalMotionComponent("clipPath"); +const MotionFeBlend = +/*@__PURE__*/ createMinimalMotionComponent("feBlend"); +const MotionFeColorMatrix = +/*@__PURE__*/ createMinimalMotionComponent("feColorMatrix"); +const MotionFeComponentTransfer = +/*@__PURE__*/ createMinimalMotionComponent("feComponentTransfer"); +const MotionFeComposite = +/*@__PURE__*/ createMinimalMotionComponent("feComposite"); +const MotionFeConvolveMatrix = +/*@__PURE__*/ createMinimalMotionComponent("feConvolveMatrix"); +const MotionFeDiffuseLighting = +/*@__PURE__*/ createMinimalMotionComponent("feDiffuseLighting"); +const MotionFeDisplacementMap = +/*@__PURE__*/ createMinimalMotionComponent("feDisplacementMap"); +const MotionFeDistantLight = +/*@__PURE__*/ createMinimalMotionComponent("feDistantLight"); +const MotionFeDropShadow = +/*@__PURE__*/ createMinimalMotionComponent("feDropShadow"); +const MotionFeFlood = +/*@__PURE__*/ createMinimalMotionComponent("feFlood"); +const MotionFeFuncA = +/*@__PURE__*/ createMinimalMotionComponent("feFuncA"); +const MotionFeFuncB = +/*@__PURE__*/ createMinimalMotionComponent("feFuncB"); +const MotionFeFuncG = +/*@__PURE__*/ createMinimalMotionComponent("feFuncG"); +const MotionFeFuncR = +/*@__PURE__*/ createMinimalMotionComponent("feFuncR"); +const MotionFeGaussianBlur = +/*@__PURE__*/ createMinimalMotionComponent("feGaussianBlur"); +const MotionFeImage = +/*@__PURE__*/ createMinimalMotionComponent("feImage"); +const MotionFeMerge = +/*@__PURE__*/ createMinimalMotionComponent("feMerge"); +const MotionFeMergeNode = +/*@__PURE__*/ createMinimalMotionComponent("feMergeNode"); +const MotionFeMorphology = +/*@__PURE__*/ createMinimalMotionComponent("feMorphology"); +const MotionFeOffset = +/*@__PURE__*/ createMinimalMotionComponent("feOffset"); +const MotionFePointLight = +/*@__PURE__*/ createMinimalMotionComponent("fePointLight"); +const MotionFeSpecularLighting = +/*@__PURE__*/ createMinimalMotionComponent("feSpecularLighting"); +const MotionFeSpotLight = +/*@__PURE__*/ createMinimalMotionComponent("feSpotLight"); +const MotionFeTile = /*@__PURE__*/ createMinimalMotionComponent("feTile"); +const MotionFeTurbulence = +/*@__PURE__*/ createMinimalMotionComponent("feTurbulence"); +const MotionForeignObject = +/*@__PURE__*/ createMinimalMotionComponent("foreignObject"); +const MotionLinearGradient = +/*@__PURE__*/ createMinimalMotionComponent("linearGradient"); +const MotionRadialGradient = +/*@__PURE__*/ createMinimalMotionComponent("radialGradient"); +const MotionTextPath = +/*@__PURE__*/ createMinimalMotionComponent("textPath"); + +exports.a = MotionA; +exports.abbr = MotionAbbr; +exports.address = MotionAddress; +exports.animate = MotionAnimate; +exports.area = MotionArea; +exports.article = MotionArticle; +exports.aside = MotionAside; +exports.audio = MotionAudio; +exports.b = MotionB; +exports.base = MotionBase; +exports.bdi = MotionBdi; +exports.bdo = MotionBdo; +exports.big = MotionBig; +exports.blockquote = MotionBlockquote; +exports.body = MotionBody; +exports.button = MotionButton; +exports.canvas = MotionCanvas; +exports.caption = MotionCaption; +exports.circle = MotionCircle; +exports.cite = MotionCite; +exports.clipPath = MotionClipPath; +exports.code = MotionCode; +exports.col = MotionCol; +exports.colgroup = MotionColgroup; +exports.create = createMinimalMotionComponent; +exports.data = MotionData; +exports.datalist = MotionDatalist; +exports.dd = MotionDd; +exports.defs = MotionDefs; +exports.del = MotionDel; +exports.desc = MotionDesc; +exports.details = MotionDetails; +exports.dfn = MotionDfn; +exports.dialog = MotionDialog; +exports.div = MotionDiv; +exports.dl = MotionDl; +exports.dt = MotionDt; +exports.ellipse = MotionEllipse; +exports.em = MotionEm; +exports.embed = MotionEmbed; +exports.feBlend = MotionFeBlend; +exports.feColorMatrix = MotionFeColorMatrix; +exports.feComponentTransfer = MotionFeComponentTransfer; +exports.feComposite = MotionFeComposite; +exports.feConvolveMatrix = MotionFeConvolveMatrix; +exports.feDiffuseLighting = MotionFeDiffuseLighting; +exports.feDisplacementMap = MotionFeDisplacementMap; +exports.feDistantLight = MotionFeDistantLight; +exports.feDropShadow = MotionFeDropShadow; +exports.feFlood = MotionFeFlood; +exports.feFuncA = MotionFeFuncA; +exports.feFuncB = MotionFeFuncB; +exports.feFuncG = MotionFeFuncG; +exports.feFuncR = MotionFeFuncR; +exports.feGaussianBlur = MotionFeGaussianBlur; +exports.feImage = MotionFeImage; +exports.feMerge = MotionFeMerge; +exports.feMergeNode = MotionFeMergeNode; +exports.feMorphology = MotionFeMorphology; +exports.feOffset = MotionFeOffset; +exports.fePointLight = MotionFePointLight; +exports.feSpecularLighting = MotionFeSpecularLighting; +exports.feSpotLight = MotionFeSpotLight; +exports.feTile = MotionFeTile; +exports.feTurbulence = MotionFeTurbulence; +exports.fieldset = MotionFieldset; +exports.figcaption = MotionFigcaption; +exports.figure = MotionFigure; +exports.filter = MotionFilter; +exports.footer = MotionFooter; +exports.foreignObject = MotionForeignObject; +exports.form = MotionForm; +exports.g = MotionG; +exports.h1 = MotionH1; +exports.h2 = MotionH2; +exports.h3 = MotionH3; +exports.h4 = MotionH4; +exports.h5 = MotionH5; +exports.h6 = MotionH6; +exports.head = MotionHead; +exports.header = MotionHeader; +exports.hgroup = MotionHgroup; +exports.hr = MotionHr; +exports.html = MotionHtml; +exports.i = MotionI; +exports.iframe = MotionIframe; +exports.image = MotionImage; +exports.img = MotionImg; +exports.input = MotionInput; +exports.ins = MotionIns; +exports.kbd = MotionKbd; +exports.keygen = MotionKeygen; +exports.label = MotionLabel; +exports.legend = MotionLegend; +exports.li = MotionLi; +exports.line = MotionLine; +exports.linearGradient = MotionLinearGradient; +exports.link = MotionLink; +exports.main = MotionMain; +exports.map = MotionMap; +exports.mark = MotionMark; +exports.marker = MotionMarker; +exports.mask = MotionMask; +exports.menu = MotionMenu; +exports.menuitem = MotionMenuitem; +exports.metadata = MotionMetadata; +exports.meter = MotionMeter; +exports.nav = MotionNav; +exports.object = MotionObject; +exports.ol = MotionOl; +exports.optgroup = MotionOptgroup; +exports.option = MotionOption; +exports.output = MotionOutput; +exports.p = MotionP; +exports.param = MotionParam; +exports.path = MotionPath; +exports.pattern = MotionPattern; +exports.picture = MotionPicture; +exports.polygon = MotionPolygon; +exports.polyline = MotionPolyline; +exports.pre = MotionPre; +exports.progress = MotionProgress; +exports.q = MotionQ; +exports.radialGradient = MotionRadialGradient; +exports.rect = MotionRect; +exports.rp = MotionRp; +exports.rt = MotionRt; +exports.ruby = MotionRuby; +exports.s = MotionS; +exports.samp = MotionSamp; +exports.script = MotionScript; +exports.section = MotionSection; +exports.select = MotionSelect; +exports.small = MotionSmall; +exports.source = MotionSource; +exports.span = MotionSpan; +exports.stop = MotionStop; +exports.strong = MotionStrong; +exports.style = MotionStyle; +exports.sub = MotionSub; +exports.summary = MotionSummary; +exports.sup = MotionSup; +exports.svg = MotionSvg; +exports.symbol = MotionSymbol; +exports.table = MotionTable; +exports.tbody = MotionTbody; +exports.td = MotionTd; +exports.text = MotionText; +exports.textPath = MotionTextPath; +exports.textarea = MotionTextarea; +exports.tfoot = MotionTfoot; +exports.th = MotionTh; +exports.thead = MotionThead; +exports.time = MotionTime; +exports.title = MotionTitle; +exports.tr = MotionTr; +exports.track = MotionTrack; +exports.tspan = MotionTspan; +exports.u = MotionU; +exports.ul = MotionUl; +exports.use = MotionUse; +exports.video = MotionVideo; +exports.view = MotionView; +exports.wbr = MotionWbr; +exports.webview = MotionWebview; diff --git a/node_modules/framer-motion/dist/cjs/mini.js b/node_modules/framer-motion/dist/cjs/mini.js new file mode 100644 index 00000000..26762cca --- /dev/null +++ b/node_modules/framer-motion/dist/cjs/mini.js @@ -0,0 +1,148 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var react = require('react'); +var motionDom = require('motion-dom'); +var motionUtils = require('motion-utils'); + +/** + * Creates a constant value over the lifecycle of a component. + * + * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer + * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` + * you can ensure that initialisers don't execute twice or more. + */ +function useConstant(init) { + const ref = react.useRef(null); + if (ref.current === null) { + ref.current = init(); + } + return ref.current; +} + +function useUnmountEffect(callback) { + return react.useEffect(() => () => callback(), []); +} + +function animateElements(elementOrSelector, keyframes, options, scope) { + const elements = motionDom.resolveElements(elementOrSelector, scope); + const numElements = elements.length; + motionUtils.invariant(Boolean(numElements), "No valid element provided."); + /** + * WAAPI doesn't support interrupting animations. + * + * Therefore, starting animations requires a three-step process: + * 1. Stop existing animations (write styles to DOM) + * 2. Resolve keyframes (read styles from DOM) + * 3. Create new animations (write styles to DOM) + * + * The hybrid `animate()` function uses AsyncAnimation to resolve + * keyframes before creating new animations, which removes style + * thrashing. Here, we have much stricter filesize constraints. + * Therefore we do this in a synchronous way that ensures that + * at least within `animate()` calls there is no style thrashing. + * + * In the motion-native-animate-mini-interrupt benchmark this + * was 80% faster than a single loop. + */ + const animationDefinitions = []; + /** + * Step 1: Build options and stop existing animations (write) + */ + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + const elementTransition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if (typeof elementTransition.delay === "function") { + elementTransition.delay = elementTransition.delay(i, numElements); + } + for (const valueName in keyframes) { + let valueKeyframes = keyframes[valueName]; + if (!Array.isArray(valueKeyframes)) { + valueKeyframes = [valueKeyframes]; + } + const valueOptions = { + ...motionDom.getValueTransition(elementTransition, valueName), + }; + valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay)); + /** + * If there's an existing animation playing on this element then stop it + * before creating a new one. + */ + const map = motionDom.getAnimationMap(element); + const key = motionDom.animationMapKey(valueName, valueOptions.pseudoElement || ""); + const currentAnimation = map.get(key); + currentAnimation && currentAnimation.stop(); + animationDefinitions.push({ + map, + key, + unresolvedKeyframes: valueKeyframes, + options: { + ...valueOptions, + element, + name: valueName, + allowFlatten: !elementTransition.type && !elementTransition.ease, + }, + }); + } + } + /** + * Step 2: Resolve keyframes (read) + */ + for (let i = 0; i < animationDefinitions.length; i++) { + const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i]; + const { element, name, pseudoElement } = animationOptions; + if (!pseudoElement && unresolvedKeyframes[0] === null) { + unresolvedKeyframes[0] = motionDom.getComputedStyle(element, name); + } + motionDom.fillWildcards(unresolvedKeyframes); + motionDom.applyPxDefaults(unresolvedKeyframes, name); + /** + * If we only have one keyframe, explicitly read the initial keyframe + * from the computed style. This is to ensure consistency with WAAPI behaviour + * for restarting animations, for instance .play() after finish, when it + * has one vs two keyframes. + */ + if (!pseudoElement && unresolvedKeyframes.length < 2) { + unresolvedKeyframes.unshift(motionDom.getComputedStyle(element, name)); + } + animationOptions.keyframes = unresolvedKeyframes; + } + /** + * Step 3: Create new animations (write) + */ + const animations = []; + for (let i = 0; i < animationDefinitions.length; i++) { + const { map, key, options: animationOptions } = animationDefinitions[i]; + const animation = new motionDom.NativeAnimation(animationOptions); + map.set(key, animation); + animation.finished.finally(() => map.delete(key)); + animations.push(animation); + } + return animations; +} + +const createScopedWaapiAnimate = (scope) => { + function scopedAnimate(elementOrSelector, keyframes, options) { + return new motionDom.GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope)); + } + return scopedAnimate; +}; + +function useAnimateMini() { + const scope = useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = useConstant(() => createScopedWaapiAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + }); + return [scope, animate]; +} + +exports.useAnimate = useAnimateMini; diff --git a/node_modules/framer-motion/dist/debug.d.ts b/node_modules/framer-motion/dist/debug.d.ts new file mode 100644 index 00000000..a4901bc1 --- /dev/null +++ b/node_modules/framer-motion/dist/debug.d.ts @@ -0,0 +1,2 @@ +export * from 'motion-dom'; +export { recordStats } from 'motion-dom'; diff --git a/node_modules/framer-motion/dist/dom-mini.d.ts b/node_modules/framer-motion/dist/dom-mini.d.ts new file mode 100644 index 00000000..a2aa22c2 --- /dev/null +++ b/node_modules/framer-motion/dist/dom-mini.d.ts @@ -0,0 +1,48 @@ +import { AnimationPlaybackOptions, Transition, MotionValue, UnresolvedValueKeyframe, ElementOrSelector, DOMKeyframesDefinition, AnimationOptions, GroupAnimationWithThen, AnimationPlaybackControlsWithThen } from 'motion-dom'; + +type ObjectTarget = { + [K in keyof O]?: O[K] | UnresolvedValueKeyframe[]; +}; +type SequenceTime = number | "<" | `+${number}` | `-${number}` | `${string}`; +type SequenceLabel = string; +interface SequenceLabelWithTime { + name: SequenceLabel; + at: SequenceTime; +} +interface At { + at?: SequenceTime; +} +type MotionValueSegment = [ + MotionValue, + UnresolvedValueKeyframe | UnresolvedValueKeyframe[] +]; +type MotionValueSegmentWithTransition = [ + MotionValue, + UnresolvedValueKeyframe | UnresolvedValueKeyframe[], + Transition & At +]; +type DOMSegment = [ElementOrSelector, DOMKeyframesDefinition]; +type DOMSegmentWithTransition = [ + ElementOrSelector, + DOMKeyframesDefinition, + AnimationOptions & At +]; +type ObjectSegment = [O, ObjectTarget]; +type ObjectSegmentWithTransition = [ + O, + ObjectTarget, + AnimationOptions & At +]; +type Segment = ObjectSegment | ObjectSegmentWithTransition | SequenceLabel | SequenceLabelWithTime | MotionValueSegment | MotionValueSegmentWithTransition | DOMSegment | DOMSegmentWithTransition; +type AnimationSequence = Segment[]; +interface SequenceOptions extends AnimationPlaybackOptions { + delay?: number; + duration?: number; + defaultTransition?: Transition; +} + +declare function animateSequence(definition: AnimationSequence, options?: SequenceOptions): GroupAnimationWithThen; + +declare const animateMini: (elementOrSelector: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions) => AnimationPlaybackControlsWithThen; + +export { animateMini as animate, animateSequence }; diff --git a/node_modules/framer-motion/dist/dom-mini.js b/node_modules/framer-motion/dist/dom-mini.js new file mode 100644 index 00000000..517a6590 --- /dev/null +++ b/node_modules/framer-motion/dist/dom-mini.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Motion={})}(this,function(t){"use strict";function e(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}function n(t){let e;return()=>(void 0===e&&(e=t()),e)}const i=t=>t,s=(t,e,n)=>{const i=e-t;return 0===i?1:(n-t)/i},o=t=>1e3*t,a=t=>t/1e3;function r(t,e){return n=t,Array.isArray(n)&&"number"!=typeof n[0]?t[((t,e,n)=>{const i=e-t;return((n-t)%i+i)%i+t})(0,t.length,e)]:t;var n}const l=(t,e,n)=>t+(e-t)*n,u=2e4;function h(t,e=100,n){const i=n({...t,keyframes:[0,e]}),s=Math.min(function(t){let e=0,n=t.next(e);for(;!n.done&&e=u?1/0:e}(i),u);return{type:"keyframes",ease:t=>i.next(s*t).value/e,duration:a(s)}}function c(t,e){const n=t[t.length-1];for(let i=1;i<=e;i++){const o=s(0,e,i);t.push(l(n,1,o))}}function f(t){const e=[0];return c(e,t.length-1),e}const m=t=>null!==t;class d{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}function p(t){for(let e=1;et.startsWith("--");const g=n(()=>void 0!==window.ScrollTimeline),A={};function b(t,e){const i=n(t);return()=>A[e]??i()}const T=b(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),v=([t,e,n,i])=>`cubic-bezier(${t}, ${e}, ${n}, ${i})`,M={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:v([0,.65,.55,1]),circOut:v([.55,0,1,.45]),backIn:v([.31,.01,.66,-.59]),backOut:v([.33,1.53,.69,.99])};function w(t,e){return t?"function"==typeof t?T()?((t,e,n=10)=>{let i="";const s=Math.max(Math.round(e/n),2);for(let e=0;eArray.isArray(t)&&"number"==typeof t[0])(t)?v(t):Array.isArray(t)?t.map(t=>w(t,e)||M.easeOut):M[t]:void 0}function k(t){return"function"==typeof t&&"applyToOptions"in t}class x extends d{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:n,keyframes:i,pseudoElement:s,allowFlatten:o=!1,finalKeyframe:a,onComplete:r}=t;this.isPseudoElement=Boolean(s),this.allowFlatten=o,this.options=t,t.type;const l=function({type:t,...e}){return k(t)&&T()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=function(t,e,n,{delay:i=0,duration:s=300,repeat:o=0,repeatType:a="loop",ease:r="easeOut",times:l}={},u){const h={[e]:n};l&&(h.offset=l);const c=w(r,s);Array.isArray(c)&&(h.easing=c);const f={delay:i,duration:s,easing:Array.isArray(c)?"linear":c,fill:"both",iterations:o+1,direction:"reverse"===a?"alternate":"normal"};return u&&(f.pseudoElement=u),t.animate(h,f)}(e,n,i,l,s),!1===l.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!s){const t=function(t,{repeat:e,repeatType:n="loop"},i,s=1){const o=t.filter(m),a=s<0||e&&"loop"!==n&&e%2==1?0:o.length-1;return a&&void 0!==i?i:o[a]}(i,this.options,a,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,n){y(e)?t.style.setProperty(e,n):t.style[e]=n}(e,n,t),this.animation.cancel()}r?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return a(Number(t))}get time(){return a(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=o(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&g()?(this.animation.timeline=t,i):e(this)}}class E{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let n=0;ne.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class O extends E{then(t,e){return this.finished.finally(t).then(()=>{})}}const S=new WeakMap,R=(t,e="")=>`${t}:${e}`;function F(t){const e=S.get(t)||new Map;return S.set(t,e),e}function W(t,e){return t?.[e]??t?.default??t}const P=new Set(["borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderRadius","radius","borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius","width","maxWidth","height","maxHeight","top","right","bottom","left","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","backgroundPositionX","backgroundPositionY"]);function B(t,e){for(let n=0;nBoolean(t&&t.getVelocity);function I(t,e,n,i){return"string"==typeof t&&function(t){return"object"==typeof t&&!Array.isArray(t)}(e)?$(t,n,i):t instanceof NodeList?Array.from(t):Array.isArray(t)?t:[t]}function N(t,e,n){return t*(e+1)}function C(t,e,n,i){return"number"==typeof e?e:e.startsWith("-")||e.startsWith("+")?Math.max(0,t+parseFloat(e)):"<"===e?n:e.startsWith("<")?Math.max(0,n+parseFloat(e.slice(1))):i.get(e)??t}function K(t,n,i,s,o,a){!function(t,n,i){for(let s=0;sn&&o.at"number"==typeof t,Y=t=>t.every(X);function G(t,e,n,i){const s=$(t,i),a=s.length,r=[];for(let t=0;te.delete(n)),l.push(s)}return l}const J=(t=>function(e,n,i){return new O(G(e,n,i,t))})();t.animate=J,t.animateSequence=function(t,e){const n=[];return function(t,{defaultTransition:e={},...n}={},i,a){const l=e.duration||.3,u=new Map,m=new Map,d={},p=new Map;let y=0,g=0,A=0;for(let n=0;n{const m=D(t),{delay:d=0,times:p=f(m),type:y="keyframes",repeat:b,repeatType:T,repeatDelay:M=0,...w}=n;let{ease:x=e.ease||"easeOut",duration:E}=n;const O="function"==typeof d?d(s,u):d,S=m.length,R=k(y)?y:a?.[y||"keyframes"];if(S<=2&&R){let t=100;if(2===S&&Y(m)){const e=m[1]-m[0];t=Math.abs(e)}const e={...w};void 0!==E&&(e.duration=o(E));const n=h(e,t,R);x=n.ease,E=n.duration}E??(E=l);const F=g+O;1===p.length&&0===p[0]&&(p[1]=1);const W=p.length-m.length;if(W>0&&c(p,W),1===m.length&&m.unshift(null),b){E=N(E,b);const t=[...m],e=[...p];x=Array.isArray(x)?[...x]:[x];const n=[...x];for(let i=0;i{for(const o in t){const a=t[o];a.sort(q);const r=[],l=[],h=[];for(let t=0;t{n.push(...G(i,t,e))}),new O(n)}}); diff --git a/node_modules/framer-motion/dist/dom.d.ts b/node_modules/framer-motion/dist/dom.d.ts new file mode 100644 index 00000000..6625ba5c --- /dev/null +++ b/node_modules/framer-motion/dist/dom.d.ts @@ -0,0 +1,151 @@ +import { UnresolvedValueKeyframe, MotionValue, Transition, ElementOrSelector, DOMKeyframesDefinition, AnimationOptions, AnimationPlaybackOptions, AnyResolvedKeyframe, AnimationScope, AnimationPlaybackControlsWithThen, ValueAnimationTransition, AnimationPlaybackControls } from 'motion-dom'; +export * from 'motion-dom'; +import { Easing, EasingFunction, Point } from 'motion-utils'; +export * from 'motion-utils'; + +type ObjectTarget = { + [K in keyof O]?: O[K] | UnresolvedValueKeyframe[]; +}; +type SequenceTime = number | "<" | `+${number}` | `-${number}` | `${string}`; +type SequenceLabel = string; +interface SequenceLabelWithTime { + name: SequenceLabel; + at: SequenceTime; +} +interface At { + at?: SequenceTime; +} +type MotionValueSegment = [ + MotionValue, + UnresolvedValueKeyframe | UnresolvedValueKeyframe[] +]; +type MotionValueSegmentWithTransition = [ + MotionValue, + UnresolvedValueKeyframe | UnresolvedValueKeyframe[], + Transition & At +]; +type DOMSegment = [ElementOrSelector, DOMKeyframesDefinition]; +type DOMSegmentWithTransition = [ + ElementOrSelector, + DOMKeyframesDefinition, + AnimationOptions & At +]; +type ObjectSegment = [O, ObjectTarget]; +type ObjectSegmentWithTransition = [ + O, + ObjectTarget, + AnimationOptions & At +]; +type Segment = ObjectSegment | ObjectSegmentWithTransition | SequenceLabel | SequenceLabelWithTime | MotionValueSegment | MotionValueSegmentWithTransition | DOMSegment | DOMSegmentWithTransition; +type AnimationSequence = Segment[]; +interface SequenceOptions extends AnimationPlaybackOptions { + delay?: number; + duration?: number; + defaultTransition?: Transition; +} +interface AbsoluteKeyframe { + value: AnyResolvedKeyframe | null; + at: number; + easing?: Easing; +} +type ValueSequence = AbsoluteKeyframe[]; +interface SequenceMap { + [key: string]: ValueSequence; +} +type ResolvedAnimationDefinition = { + keyframes: { + [key: string]: UnresolvedValueKeyframe[]; + }; + transition: { + [key: string]: Transition; + }; +}; +type ResolvedAnimationDefinitions = Map; + +/** + * Creates an animation function that is optionally scoped + * to a specific element. + */ +declare function createScopedAnimate(scope?: AnimationScope): { + (sequence: AnimationSequence, options?: SequenceOptions): AnimationPlaybackControlsWithThen; + (value: string | MotionValue, keyframes: string | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: number | MotionValue, keyframes: number | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: V | MotionValue, keyframes: V | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (element: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions): AnimationPlaybackControlsWithThen; + (object: O | O[], keyframes: ObjectTarget, options?: AnimationOptions): AnimationPlaybackControlsWithThen; +}; +declare const animate: { + (sequence: AnimationSequence, options?: SequenceOptions): AnimationPlaybackControlsWithThen; + (value: string | MotionValue, keyframes: string | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: number | MotionValue, keyframes: number | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: V | MotionValue, keyframes: V | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (element: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions): AnimationPlaybackControlsWithThen; + (object: O | O[], keyframes: ObjectTarget, options?: AnimationOptions): AnimationPlaybackControlsWithThen; +}; + +declare const animateMini: (elementOrSelector: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions) => AnimationPlaybackControlsWithThen; + +interface ScrollOptions { + source?: HTMLElement; + container?: Element; + target?: Element; + axis?: "x" | "y"; + offset?: ScrollOffset; +} +type OnScrollProgress = (progress: number) => void; +type OnScrollWithInfo = (progress: number, info: ScrollInfo) => void; +type OnScroll = OnScrollProgress | OnScrollWithInfo; +interface AxisScrollInfo { + current: number; + offset: number[]; + progress: number; + scrollLength: number; + velocity: number; + targetOffset: number; + targetLength: number; + containerLength: number; + interpolatorOffsets?: number[]; + interpolate?: EasingFunction; +} +interface ScrollInfo { + time: number; + x: AxisScrollInfo; + y: AxisScrollInfo; +} +type OnScrollInfo = (info: ScrollInfo) => void; +type SupportedEdgeUnit = "px" | "vw" | "vh" | "%"; +type EdgeUnit = `${number}${SupportedEdgeUnit}`; +type NamedEdges = "start" | "end" | "center"; +type EdgeString = NamedEdges | EdgeUnit | `${number}`; +type Edge = EdgeString | number; +type ProgressIntersection = [number, number]; +type Intersection = `${Edge} ${Edge}`; +type ScrollOffset = Array; +interface ScrollInfoOptions { + container?: Element; + target?: Element; + axis?: "x" | "y"; + offset?: ScrollOffset; +} + +declare function scroll(onScroll: OnScroll | AnimationPlaybackControls, { axis, container, ...options }?: ScrollOptions): VoidFunction; + +declare function scrollInfo(onScroll: OnScrollInfo, { container, ...options }?: ScrollInfoOptions): VoidFunction; + +type ViewChangeHandler = (entry: IntersectionObserverEntry) => void; +type MarginValue = `${number}${"px" | "%"}`; +type MarginType = MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`; +interface InViewOptions { + root?: Element | Document; + margin?: MarginType; + amount?: "some" | "all" | number; +} +declare function inView(elementOrSelector: ElementOrSelector, onStart: (element: Element, entry: IntersectionObserverEntry) => void | ViewChangeHandler, { root, margin: rootMargin, amount }?: InViewOptions): VoidFunction; + +type DelayedFunction = (overshoot: number) => void; +declare function delayInSeconds(callback: DelayedFunction, timeout: number): () => void; + +declare const distance: (a: number, b: number) => number; +declare function distance2D(a: Point, b: Point): number; + +export { type AbsoluteKeyframe, type AnimationSequence, type At, type DOMSegment, type DOMSegmentWithTransition, type DelayedFunction, type MotionValueSegment, type MotionValueSegmentWithTransition, type ObjectSegment, type ObjectSegmentWithTransition, type ObjectTarget, type ResolvedAnimationDefinition, type ResolvedAnimationDefinitions, type Segment, type SequenceLabel, type SequenceLabelWithTime, type SequenceMap, type SequenceOptions, type SequenceTime, type ValueSequence, animate, animateMini, createScopedAnimate, delayInSeconds as delay, distance, distance2D, inView, scroll, scrollInfo }; diff --git a/node_modules/framer-motion/dist/dom.js b/node_modules/framer-motion/dist/dom.js new file mode 100644 index 00000000..cd892930 --- /dev/null +++ b/node_modules/framer-motion/dist/dom.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Motion={})}(this,function(t){"use strict";function e(t,e){-1===t.indexOf(e)&&t.push(e)}function n(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}const s=(t,e,n)=>n>e?e:n{};const i={},o=t=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t);function a(t){return"object"==typeof t&&null!==t}const l=t=>/^0[^.\s]+$/u.test(t);function u(t){let e;return()=>(void 0===e&&(e=t()),e)}const c=t=>t,h=(t,e)=>n=>e(t(n)),d=(...t)=>t.reduce(h),p=(t,e,n)=>{const s=e-t;return 0===s?1:(n-t)/s};class f{constructor(){this.subscriptions=[]}add(t){return e(this.subscriptions,t),()=>n(this.subscriptions,t)}notify(t,e,n){const s=this.subscriptions.length;if(s)if(1===s)this.subscriptions[0](t,e,n);else for(let r=0;r1e3*t,g=t=>t/1e3;function y(t,e){return e?t*(1e3/e):0}const v=new Set;const w=(t,e,n)=>{const s=e-t;return((n-t)%s+s)%s+t},b=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function T(t,e,n,s){if(t===e&&n===s)return c;const r=e=>function(t,e,n,s,r){let i,o,a=0;do{o=e+(n-e)/2,i=b(o,s,r)-t,i>0?n=o:e=o}while(Math.abs(i)>1e-7&&++a<12);return o}(e,0,1,t,n);return t=>0===t||1===t?t:b(r(t),e,s)}const x=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,V=t=>e=>1-t(1-e),M=T(.33,1.53,.69,.99),S=V(M),A=x(S),k=t=>(t*=2)<1?.5*S(t):.5*(2-Math.pow(2,-10*(t-1))),E=t=>1-Math.sin(Math.acos(t)),P=V(E),C=x(E),O=T(.42,0,1,1),F=T(0,0,.58,1),R=T(.42,0,.58,1);const B=t=>Array.isArray(t)&&"number"!=typeof t[0];function D(t,e){return B(t)?t[w(0,t.length,e)]:t}const L=t=>Array.isArray(t)&&"number"==typeof t[0],W={linear:c,easeIn:O,easeInOut:R,easeOut:F,circIn:E,circInOut:C,circOut:P,backIn:S,backInOut:A,backOut:M,anticipate:k},I=t=>{if(L(t)){t.length;const[e,n,s,r]=t;return T(e,n,s,r)}return"string"==typeof t?W[t]:t},j=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],N={value:null,addProjectionMetrics:null};function K(t,e){let n=!1,s=!0;const r={delta:0,timestamp:0,isProcessing:!1},o=()=>n=!0,a=j.reduce((t,n)=>(t[n]=function(t,e){let n=new Set,s=new Set,r=!1,i=!1;const o=new WeakSet;let a={delta:0,timestamp:0,isProcessing:!1},l=0;function u(e){o.has(e)&&(c.schedule(e),t()),l++,e(a)}const c={schedule:(t,e=!1,i=!1)=>{const a=i&&r?n:s;return e&&o.add(t),a.has(t)||a.add(t),t},cancel:t=>{s.delete(t),o.delete(t)},process:t=>{a=t,r?i=!0:(r=!0,[n,s]=[s,n],n.forEach(u),e&&N.value&&N.value.frameloop[e].push(l),l=0,n.clear(),r=!1,i&&(i=!1,c.process(t)))}};return c}(o,e?n:void 0),t),{}),{setup:l,read:u,resolveKeyframes:c,preUpdate:h,update:d,preRender:p,render:f,postRender:m}=a,g=()=>{const o=i.useManualTiming?r.timestamp:performance.now();n=!1,i.useManualTiming||(r.delta=s?1e3/60:Math.max(Math.min(o-r.timestamp,40),1)),r.timestamp=o,r.isProcessing=!0,l.process(r),u.process(r),c.process(r),h.process(r),d.process(r),p.process(r),f.process(r),m.process(r),r.isProcessing=!1,n&&e&&(s=!1,t(g))};return{schedule:j.reduce((e,i)=>{const o=a[i];return e[i]=(e,i=!1,a=!1)=>(n||(n=!0,s=!0,r.isProcessing||t(g)),o.schedule(e,i,a)),e},{}),cancel:t=>{for(let e=0;e(void 0===X&&q.set(Y.isProcessing||i.useManualTiming?Y.timestamp:performance.now()),X),set:t=>{X=t,queueMicrotask(H)}},G={layout:0,mainThread:0,waapi:0},Z=t=>e=>"string"==typeof e&&e.startsWith(t),_=Z("--"),J=Z("var(--"),Q=t=>!!J(t)&&tt.test(t.split("/*")[0].trim()),tt=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,et={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},nt={...et,transform:t=>s(0,1,t)},st={...et,default:1},rt=t=>Math.round(1e5*t)/1e5,it=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const ot=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,at=(t,e)=>n=>Boolean("string"==typeof n&&ot.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),lt=(t,e,n)=>s=>{if("string"!=typeof s)return s;const[r,i,o,a]=s.match(it);return{[t]:parseFloat(r),[e]:parseFloat(i),[n]:parseFloat(o),alpha:void 0!==a?parseFloat(a):1}},ut={...et,transform:t=>Math.round((t=>s(0,255,t))(t))},ct={test:at("rgb","red"),parse:lt("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:s=1})=>"rgba("+ut.transform(t)+", "+ut.transform(e)+", "+ut.transform(n)+", "+rt(nt.transform(s))+")"};const ht={test:at("#"),parse:function(t){let e="",n="",s="",r="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),s=t.substring(5,7),r=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),s=t.substring(3,4),r=t.substring(4,5),e+=e,n+=n,s+=s,r+=r),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(s,16),alpha:r?parseInt(r,16)/255:1}},transform:ct.transform},dt=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),pt=dt("deg"),ft=dt("%"),mt=dt("px"),gt=dt("vh"),yt=dt("vw"),vt=(()=>({...ft,parse:t=>ft.parse(t)/100,transform:t=>ft.transform(100*t)}))(),wt={test:at("hsl","hue"),parse:lt("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:s=1})=>"hsla("+Math.round(t)+", "+ft.transform(rt(e))+", "+ft.transform(rt(n))+", "+rt(nt.transform(s))+")"},bt={test:t=>ct.test(t)||ht.test(t)||wt.test(t),parse:t=>ct.test(t)?ct.parse(t):wt.test(t)?wt.parse(t):ht.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?ct.transform(t):wt.transform(t),getAnimatableNone:t=>{const e=bt.parse(t);return e.alpha=0,bt.transform(e)}},Tt=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const xt="number",Vt="color",Mt=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function St(t){const e=t.toString(),n=[],s={color:[],number:[],var:[]},r=[];let i=0;const o=e.replace(Mt,t=>(bt.test(t)?(s.color.push(i),r.push(Vt),n.push(bt.parse(t))):t.startsWith("var(")?(s.var.push(i),r.push("var"),n.push(t)):(s.number.push(i),r.push(xt),n.push(parseFloat(t))),++i,"${}")).split("${}");return{values:n,split:o,indexes:s,types:r}}function At(t){return St(t).values}function kt(t){const{split:e,types:n}=St(t),s=e.length;return t=>{let r="";for(let i=0;i"number"==typeof t?0:bt.test(t)?bt.getAnimatableNone(t):t;const Pt={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(it)?.length||0)+(t.match(Tt)?.length||0)>0},parse:At,createTransformer:kt,getAnimatableNone:function(t){const e=At(t);return kt(t)(e.map(Et))}};function Ct(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function Ot({hue:t,saturation:e,lightness:n,alpha:s}){t/=360,n/=100;let r=0,i=0,o=0;if(e/=100){const s=n<.5?n*(1+e):n+e-n*e,a=2*n-s;r=Ct(a,s,t+1/3),i=Ct(a,s,t),o=Ct(a,s,t-1/3)}else r=i=o=n;return{red:Math.round(255*r),green:Math.round(255*i),blue:Math.round(255*o),alpha:s}}function Ft(t,e){return n=>n>0?e:t}const Rt=(t,e,n)=>t+(e-t)*n,Bt=(t,e,n)=>{const s=t*t,r=n*(e*e-s)+s;return r<0?0:Math.sqrt(r)},Dt=[ht,ct,wt];function Lt(t){const e=(n=t,Dt.find(t=>t.test(n)));var n;if(!Boolean(e))return!1;let s=e.parse(t);return e===wt&&(s=Ot(s)),s}const Wt=(t,e)=>{const n=Lt(t),s=Lt(e);if(!n||!s)return Ft(t,e);const r={...n};return t=>(r.red=Bt(n.red,s.red,t),r.green=Bt(n.green,s.green,t),r.blue=Bt(n.blue,s.blue,t),r.alpha=Rt(n.alpha,s.alpha,t),ct.transform(r))},It=new Set(["none","hidden"]);function jt(t,e){return It.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}function Nt(t,e){return n=>Rt(t,e,n)}function Kt(t){return"number"==typeof t?Nt:"string"==typeof t?Q(t)?Ft:bt.test(t)?Wt:Yt:Array.isArray(t)?$t:"object"==typeof t?bt.test(t)?Wt:Ut:Ft}function $t(t,e){const n=[...t],s=n.length,r=t.map((t,n)=>Kt(t)(t,e[n]));return t=>{for(let e=0;e{for(const e in s)n[e]=s[e](t);return n}}const Yt=(t,e)=>{const n=Pt.createTransformer(e),s=St(t),r=St(e);return s.indexes.var.length===r.indexes.var.length&&s.indexes.color.length===r.indexes.color.length&&s.indexes.number.length>=r.indexes.number.length?It.has(t)&&!r.values.length||It.has(e)&&!s.values.length?jt(t,e):d($t(function(t,e){const n=[],s={color:0,var:0,number:0};for(let r=0;r{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>$.update(e,t),stop:()=>U(e),now:()=>Y.isProcessing?Y.timestamp:q.now()}},Ht=(t,e,n=10)=>{let s="";const r=Math.max(Math.round(e/n),2);for(let e=0;e=qt?1/0:e}function Zt(t,e=100,n){const s=n({...t,keyframes:[0,e]}),r=Math.min(Gt(s),qt);return{type:"keyframes",ease:t=>s.next(r*t).value/e,duration:g(r)}}function _t(t,e,n){const s=Math.max(e-5,0);return y(n-t(s),e-s)}const Jt=100,Qt=10,te=1,ee=0,ne=800,se=.3,re=.3,ie={granular:.01,default:2},oe={granular:.005,default:.5},ae=.01,le=10,ue=.05,ce=1,he=.001;function de({duration:t=ne,bounce:e=se,velocity:n=ee,mass:r=te}){let i,o,a=1-e;a=s(ue,ce,a),t=s(ae,le,g(t)),a<1?(i=e=>{const s=e*a,r=s*t,i=s-n,o=fe(e,a),l=Math.exp(-r);return he-i/o*l},o=e=>{const s=e*a*t,r=s*n+n,o=Math.pow(a,2)*Math.pow(e,2)*t,l=Math.exp(-s),u=fe(Math.pow(e,2),a);return(-i(e)+he>0?-1:1)*((r-o)*l)/u}):(i=e=>Math.exp(-e*t)*((e-n)*t+1)-.001,o=e=>Math.exp(-e*t)*(t*t*(n-e)));const l=function(t,e,n){let s=n;for(let n=1;nvoid 0!==t[e])}function ve(t=re,e=se){const n="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:r,restDelta:i}=n;const o=n.keyframes[0],a=n.keyframes[n.keyframes.length-1],l={done:!1,value:o},{stiffness:u,damping:c,mass:h,duration:d,velocity:p,isResolvedFromDuration:f}=function(t){let e={velocity:ee,stiffness:Jt,damping:Qt,mass:te,isResolvedFromDuration:!1,...t};if(!ye(t,ge)&&ye(t,me))if(t.visualDuration){const n=t.visualDuration,r=2*Math.PI/(1.2*n),i=r*r,o=2*s(.05,1,1-(t.bounce||0))*Math.sqrt(i);e={...e,mass:te,stiffness:i,damping:o}}else{const n=de(t);e={...e,...n,mass:te},e.isResolvedFromDuration=!0}return e}({...n,velocity:-g(n.velocity||0)}),y=p||0,v=c/(2*Math.sqrt(u*h)),w=a-o,b=g(Math.sqrt(u/h)),T=Math.abs(w)<5;let x;if(r||(r=T?ie.granular:ie.default),i||(i=T?oe.granular:oe.default),v<1){const t=fe(b,v);x=e=>{const n=Math.exp(-v*b*e);return a-n*((y+v*b*w)/t*Math.sin(t*e)+w*Math.cos(t*e))}}else if(1===v)x=t=>a-Math.exp(-b*t)*(w+(y+b*w)*t);else{const t=b*Math.sqrt(v*v-1);x=e=>{const n=Math.exp(-v*b*e),s=Math.min(t*e,300);return a-n*((y+v*b*w)*Math.sinh(s)+t*w*Math.cosh(s))/t}}const V={calculatedDuration:f&&d||null,next:t=>{const e=x(t);if(f)l.done=t>=d;else{let n=0===t?y:0;v<1&&(n=0===t?m(y):_t(x,t,e));const s=Math.abs(n)<=r,o=Math.abs(a-e)<=i;l.done=s&&o}return l.value=l.done?a:e,l},toString:()=>{const t=Math.min(Gt(V),qt),e=Ht(e=>V.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return V}function we({keyframes:t,velocity:e=0,power:n=.8,timeConstant:s=325,bounceDamping:r=10,bounceStiffness:i=500,modifyTarget:o,min:a,max:l,restDelta:u=.5,restSpeed:c}){const h=t[0],d={done:!1,value:h},p=t=>void 0===a?l:void 0===l||Math.abs(a-t)-f*Math.exp(-t/s),v=t=>g+y(t),w=t=>{const e=y(t),n=v(t);d.done=Math.abs(e)<=u,d.value=d.done?g:n};let b,T;const x=t=>{var e;(e=d.value,void 0!==a&&el)&&(b=t,T=ve({keyframes:[d.value,p(d.value)],velocity:_t(v,t,d.value),damping:r,stiffness:i,restDelta:u,restSpeed:c}))};return x(0),{calculatedDuration:null,next:t=>{let e=!1;return T||void 0!==b||(e=!0,w(t),x(t)),void 0!==b&&t>=b?T.next(t-b):(!e&&w(t),d)}}}function be(t,e,{clamp:n=!0,ease:r,mixer:o}={}){const a=t.length;if(e.length,1===a)return()=>e[0];if(2===a&&e[0]===e[1])return()=>e[1];const l=t[0]===t[1];t[0]>t[a-1]&&(t=[...t].reverse(),e=[...e].reverse());const u=function(t,e,n){const s=[],r=n||i.mix||zt,o=t.length-1;for(let n=0;n{if(l&&n1)for(;sf(s(t[0],t[a-1],e)):f}function Te(t,e){const n=t[t.length-1];for(let s=1;s<=e;s++){const r=p(0,e,s);t.push(Rt(n,1,r))}}function xe(t){const e=[0];return Te(e,t.length-1),e}function Ve(t,e){return t.map(t=>t*e)}function Me(t,e){return t.map(()=>e||R).splice(0,t.length-1)}function Se({duration:t=300,keyframes:e,times:n,ease:s="easeInOut"}){const r=B(s)?s.map(I):I(s),i={done:!1,value:e[0]},o=be(Ve(n&&n.length===e.length?n:xe(e),t),e,{ease:Array.isArray(r)?r:Me(e,r)});return{calculatedDuration:t,next:e=>(i.value=o(e),i.done=e>=t,i)}}ve.applyToOptions=t=>{const e=Zt(t,100,ve);return t.ease=e.ease,t.duration=m(e.duration),t.type="keyframes",t};const Ae=t=>null!==t;function ke(t,{repeat:e,repeatType:n="loop"},s,r=1){const i=t.filter(Ae),o=r<0||e&&"loop"!==n&&e%2==1?0:i.length-1;return o&&void 0!==s?s:i[o]}const Ee={decay:we,inertia:we,tween:Se,keyframes:Se,spring:ve};function Pe(t){"string"==typeof t.type&&(t.type=Ee[t.type])}class Ce{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Oe=t=>t/100;class Fe extends Ce{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==q.now()&&this.tick(q.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},G.mainThread++,this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;Pe(t);const{type:e=Se,repeat:n=0,repeatDelay:s=0,repeatType:r,velocity:i=0}=t;let{keyframes:o}=t;const a=e||Se;a!==Se&&"number"!=typeof o[0]&&(this.mixKeyframes=d(Oe,zt(o[0],o[1])),o=[0,100]);const l=a({...t,keyframes:o});"mirror"===r&&(this.mirroredGenerator=a({...t,keyframes:[...o].reverse(),velocity:-i})),null===l.calculatedDuration&&(l.calculatedDuration=Gt(l));const{calculatedDuration:u}=l;this.calculatedDuration=u,this.resolvedDuration=u+s,this.totalDuration=this.resolvedDuration*(n+1)-s,this.generator=l}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,e=!1){const{generator:n,totalDuration:r,mixKeyframes:i,mirroredGenerator:o,resolvedDuration:a,calculatedDuration:l}=this;if(null===this.startTime)return n.next(0);const{delay:u=0,keyframes:c,repeat:h,repeatType:d,repeatDelay:p,type:f,onUpdate:m,finalKeyframe:g}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-r/this.speed,this.startTime)),e?this.currentTime=t:this.updateTime(t);const y=this.currentTime-u*(this.playbackSpeed>=0?1:-1),v=this.playbackSpeed>=0?y<0:y>r;this.currentTime=Math.max(y,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=r);let w=this.currentTime,b=n;if(h){const t=Math.min(this.currentTime,r)/a;let e=Math.floor(t),n=t%1;!n&&t>=1&&(n=1),1===n&&e--,e=Math.min(e,h+1);Boolean(e%2)&&("reverse"===d?(n=1-n,p&&(n-=p/a)):"mirror"===d&&(b=o)),w=s(0,1,n)*a}const T=v?{done:!1,value:c[0]}:b.next(w);i&&(T.value=i(T.value));let{done:x}=T;v||null===l||(x=this.playbackSpeed>=0?this.currentTime>=r:this.currentTime<=0);const V=null===this.holdTime&&("finished"===this.state||"running"===this.state&&x);return V&&f!==we&&(T.value=ke(c,this.options,g,this.speed)),m&&m(T.value),V&&this.finish(),T}then(t,e){return this.finished.then(t,e)}get duration(){return g(this.calculatedDuration)}get time(){return g(this.currentTime)}set time(t){t=m(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(q.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=g(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=Xt,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const n=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=n):null!==this.holdTime?this.startTime=n-this.holdTime:this.startTime||(this.startTime=e??n),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(q.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null,G.mainThread--}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}function Re(t){for(let e=1;e180*t/Math.PI,De=t=>{const e=Be(Math.atan2(t[1],t[0]));return We(e)},Le={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:De,rotateZ:De,skewX:t=>Be(Math.atan(t[1])),skewY:t=>Be(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},We=t=>((t%=360)<0&&(t+=360),t),Ie=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),je=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),Ne={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:Ie,scaleY:je,scale:t=>(Ie(t)+je(t))/2,rotateX:t=>We(Be(Math.atan2(t[6],t[5]))),rotateY:t=>We(Be(Math.atan2(-t[2],t[0]))),rotateZ:De,rotate:De,skewX:t=>Be(Math.atan(t[4])),skewY:t=>Be(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function Ke(t){return t.includes("scale")?1:0}function $e(t,e){if(!t||"none"===t)return Ke(e);const n=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let s,r;if(n)s=Ne,r=n;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);s=Le,r=e}if(!r)return Ke(e);const i=s[e],o=r[1].split(",").map(Ye);return"function"==typeof i?i(o):o[i]}const Ue=(t,e)=>{const{transform:n="none"}=getComputedStyle(t);return $e(n,e)};function Ye(t){return parseFloat(t.trim())}const ze=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],Xe=(()=>new Set(ze))(),He=t=>t===et||t===mt,qe=new Set(["x","y","z"]),Ge=ze.filter(t=>!qe.has(t));const Ze={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>$e(e,"x"),y:(t,{transform:e})=>$e(e,"y")};Ze.translateX=Ze.x,Ze.translateY=Ze.y;const _e=new Set;let Je=!1,Qe=!1,tn=!1;function en(){if(Qe){const t=Array.from(_e).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return Ge.forEach(n=>{const s=t.getValue(n);void 0!==s&&(e.push([n,s.get()]),s.set(n.startsWith("scale")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{t.getValue(e)?.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}Qe=!1,Je=!1,_e.forEach(t=>t.complete(tn)),_e.clear()}function nn(){_e.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(Qe=!0)})}function sn(){tn=!0,nn(),en(),tn=!1}class rn{constructor(t,e,n,s,r,i=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=s,this.element=r,this.isAsync=i}scheduleResolve(){this.state="scheduled",this.isAsync?(_e.add(this),Je||(Je=!0,$.read(nn),$.resolveKeyframes(en))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:s}=this;if(null===t[0]){const r=s?.get(),i=t[t.length-1];if(void 0!==r)t[0]=r;else if(n&&e){const s=n.readValue(e,i);null!=s&&(t[0]=s)}void 0===t[0]&&(t[0]=i),s&&void 0===r&&s.set(t[0])}Re(t)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(t=!1){this.state="complete",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,t),_e.delete(this)}cancel(){"scheduled"===this.state&&(_e.delete(this),this.state="pending")}resume(){"pending"===this.state&&this.scheduleResolve()}}const on=t=>t.startsWith("--");function an(t,e,n){on(e)?t.style.setProperty(e,n):t.style[e]=n}const ln=u(()=>void 0!==window.ScrollTimeline),un={};function cn(t,e){const n=u(t);return()=>un[e]??n()}const hn=cn(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),dn=([t,e,n,s])=>`cubic-bezier(${t}, ${e}, ${n}, ${s})`,pn={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:dn([0,.65,.55,1]),circOut:dn([.55,0,1,.45]),backIn:dn([.31,.01,.66,-.59]),backOut:dn([.33,1.53,.69,.99])};function fn(t,e){return t?"function"==typeof t?hn()?Ht(t,e):"ease-out":L(t)?dn(t):Array.isArray(t)?t.map(t=>fn(t,e)||pn.easeOut):pn[t]:void 0}function mn(t,e,n,{delay:s=0,duration:r=300,repeat:i=0,repeatType:o="loop",ease:a="easeOut",times:l}={},u=void 0){const c={[e]:n};l&&(c.offset=l);const h=fn(a,r);Array.isArray(h)&&(c.easing=h),N.value&&G.waapi++;const d={delay:s,duration:r,easing:Array.isArray(h)?"linear":h,fill:"both",iterations:i+1,direction:"reverse"===o?"alternate":"normal"};u&&(d.pseudoElement=u);const p=t.animate(c,d);return N.value&&p.finished.finally(()=>{G.waapi--}),p}function gn(t){return"function"==typeof t&&"applyToOptions"in t}function yn({type:t,...e}){return gn(t)&&hn()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}class vn extends Ce{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:n,keyframes:s,pseudoElement:r,allowFlatten:i=!1,finalKeyframe:o,onComplete:a}=t;this.isPseudoElement=Boolean(r),this.allowFlatten=i,this.options=t,t.type;const l=yn(t);this.animation=mn(e,n,s,l,r),!1===l.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!r){const t=ke(s,this.options,o,this.speed);this.updateMotionValue?this.updateMotionValue(t):an(e,n,t),this.animation.cancel()}a?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return g(Number(t))}get time(){return g(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=m(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&ln()?(this.animation.timeline=t,c):e(this)}}const wn={anticipate:k,backInOut:A,circInOut:C};function bn(t){"string"==typeof t.ease&&t.ease in wn&&(t.ease=wn[t.ease])}class Tn extends vn{constructor(t){bn(t),Pe(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:n,onComplete:s,element:r,...i}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const o=new Fe({...i,autoplay:!1}),a=m(this.finishedTime??this.time);e.setWithVelocity(o.sample(a-10).value,o.sample(a).value,10),o.stop()}}const xn=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!Pt.test(t)&&"0"!==t||t.startsWith("url(")));function Vn(t){return a(t)&&"offsetHeight"in t}const Mn=new Set(["opacity","clipPath","filter","transform"]),Sn=u(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));function An(t){const{motionValue:e,name:n,repeatDelay:s,repeatType:r,damping:i,type:o}=t;if(!Vn(e?.owner?.current))return!1;const{onUpdate:a,transformTemplate:l}=e.owner.getProps();return Sn()&&n&&Mn.has(n)&&("transform"!==n||!l)&&!a&&!s&&"mirror"!==r&&0!==i&&"inertia"!==o}class kn extends Ce{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:s=0,repeatDelay:r=0,repeatType:i="loop",keyframes:o,name:a,motionValue:l,element:u,...c}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=q.now();const h={autoplay:t,delay:e,type:n,repeat:s,repeatDelay:r,repeatType:i,name:a,motionValue:l,element:u,...c},d=u?.KeyframeResolver||rn;this.keyframeResolver=new d(o,(t,e,n)=>this.onKeyframesResolved(t,e,h,!n),a,l,u),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,e,n,s){this.keyframeResolver=void 0;const{name:r,type:o,velocity:a,delay:l,isHandoff:u,onUpdate:h}=n;this.resolvedAt=q.now(),function(t,e,n,s){const r=t[0];if(null===r)return!1;if("display"===e||"visibility"===e)return!0;const i=t[t.length-1],o=xn(r,e),a=xn(i,e);return!(!o||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:e,...n,keyframes:t},p=!u&&An(d)?new Tn({...d,element:d.motionValue.owner.current}):new Fe(d);p.finished.then(()=>this.notifyFinished()).catch(c),this.pendingTimeline&&(this.stopTimeline=p.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=p}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),sn()),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}}class En{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let n=0;ne.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class Pn extends En{then(t,e){return this.finished.finally(t).then(()=>{})}}class Cn extends vn{constructor(t){super(),this.animation=t,t.onfinish=()=>{this.finishedTime=this.time,this.notifyFinished()}}}const On=new WeakMap,Fn=(t,e="")=>`${t}:${e}`;function Rn(t){const e=On.get(t)||new Map;return On.set(t,e),e}const Bn=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function Dn(t){const e=Bn.exec(t);if(!e)return[,];const[,n,s,r]=e;return[`--${n??s}`,r]}function Ln(t,e,n=1){const[s,r]=Dn(t);if(!s)return;const i=window.getComputedStyle(e).getPropertyValue(s);if(i){const t=i.trim();return o(t)?parseFloat(t):t}return Q(r)?Ln(r,e,n+1):r}function Wn(t,e){return t?.[e]??t?.default??t}const In=new Set(["width","height","top","left","right","bottom",...ze]),jn=t=>e=>e.test(t),Nn=[et,mt,ft,pt,yt,gt,{test:t=>"auto"===t,parse:t=>t}],Kn=t=>Nn.find(jn(t));function $n(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||l(t))}const Un=new Set(["brightness","contrast","saturate","opacity"]);function Yn(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[s]=n.match(it)||[];if(!s)return t;const r=n.replace(s,"");let i=Un.has(e)?1:0;return s!==n&&(i*=100),e+"("+i+r+")"}const zn=/\b([a-z-]*)\(.*?\)/gu,Xn={...Pt,getAnimatableNone:t=>{const e=t.match(zn);return e?e.map(Yn).join(" "):t}},Hn={...et,transform:Math.round},qn={rotate:pt,rotateX:pt,rotateY:pt,rotateZ:pt,scale:st,scaleX:st,scaleY:st,scaleZ:st,skew:pt,skewX:pt,skewY:pt,distance:mt,translateX:mt,translateY:mt,translateZ:mt,x:mt,y:mt,z:mt,perspective:mt,transformPerspective:mt,opacity:nt,originX:vt,originY:vt,originZ:mt},Gn={borderWidth:mt,borderTopWidth:mt,borderRightWidth:mt,borderBottomWidth:mt,borderLeftWidth:mt,borderRadius:mt,radius:mt,borderTopLeftRadius:mt,borderTopRightRadius:mt,borderBottomRightRadius:mt,borderBottomLeftRadius:mt,width:mt,maxWidth:mt,height:mt,maxHeight:mt,top:mt,right:mt,bottom:mt,left:mt,padding:mt,paddingTop:mt,paddingRight:mt,paddingBottom:mt,paddingLeft:mt,margin:mt,marginTop:mt,marginRight:mt,marginBottom:mt,marginLeft:mt,backgroundPositionX:mt,backgroundPositionY:mt,...qn,zIndex:Hn,fillOpacity:nt,strokeOpacity:nt,numOctaves:Hn},Zn={...Gn,color:bt,backgroundColor:bt,outlineColor:bt,fill:bt,stroke:bt,borderColor:bt,borderTopColor:bt,borderRightColor:bt,borderBottomColor:bt,borderLeftColor:bt,filter:Xn,WebkitFilter:Xn},_n=t=>Zn[t];function Jn(t,e){let n=_n(t);return n!==Xn&&(n=Pt),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const Qn=new Set(["auto","none","0"]);class ts extends rn{constructor(t,e,n,s,r){super(t,e,n,s,r,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{t.getValue(e).set(n)}),this.resolveNoneKeyframes()}}const es=new Set(["borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderRadius","radius","borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius","width","maxWidth","height","maxHeight","top","right","bottom","left","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","backgroundPositionX","backgroundPositionY"]);function ns(t,e){for(let n=0;n{try{document.createElement("div").animate({opacity:[1]})}catch(t){return!1}return!0}),rs=new Set(["opacity","clipPath","filter","transform"]);function is(t,e,n){if(t instanceof EventTarget)return[t];if("string"==typeof t){let s=document;e&&(s=e.current);const r=n?.[t]??s.querySelectorAll(t);return r?Array.from(r):[]}return Array.from(t)}function os(t){return(e,n)=>{const s=is(e),r=[];for(const e of s){const s=t(e,n);r.push(s)}return()=>{for(const t of r)t()}}}const as=(t,e)=>e&&"number"==typeof t?e.transform(t):t;class ls{constructor(){this.latest={},this.values=new Map}set(t,e,n,s,r=!0){const i=this.values.get(t);i&&i.onRemove();const o=()=>{const s=e.get();this.latest[t]=r?as(s,Gn[t]):s,n&&$.render(n)};o();const a=e.on("change",o);s&&e.addDependent(s);const l=()=>{a(),n&&U(n),this.values.delete(t),s&&e.removeDependent(s)};return this.values.set(t,{value:e,onRemove:l}),l}get(t){return this.values.get(t)?.value}destroy(){for(const t of this.values.values())t.onRemove()}}function us(t){const e=new WeakMap,n=[];return(s,r)=>{const i=e.get(s)??new ls;e.set(s,i);for(const e in r){const o=r[e],a=t(s,i,e,o);n.push(a)}return()=>{for(const t of n)t()}}}const cs=(t,e,n,s)=>{const r=function(t,e){if(!(e in t))return!1;const n=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(t),e)||Object.getOwnPropertyDescriptor(t,e);return n&&"function"==typeof n.set}(t,n),i=r?n:n.startsWith("data")||n.startsWith("aria")?n.replace(/([A-Z])/g,t=>`-${t.toLowerCase()}`):n;const o=r?()=>{t[i]=e.latest[n]}:()=>{const s=e.latest[n];null==s?t.removeAttribute(i):t.setAttribute(i,String(s))};return e.set(n,s,o)},hs=os(us(cs)),ds=us((t,e,n,s)=>e.set(n,s,()=>{t[n]=e.latest[n]},void 0,!1)),ps={current:void 0};class fs{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const n=q.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=q.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new f);const n=this.events[t].add(e);return"change"===t?()=>{n(),$.read(()=>{this.events.change.getSize()||this.stop()})}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return ps.current&&ps.current.push(this),this.current}getPrevious(){return this.prev}getVelocity(){const t=q.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return y(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function ms(t,e){return new fs(t,e)}const gs={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"};const ys=new Set(["originX","originY","originZ"]),vs=(t,e,n,s)=>{let r,i;return Xe.has(n)?(e.get("transform")||(Vn(t)||e.get("transformBox")||vs(t,e,"transformBox",new fs("fill-box")),e.set("transform",new fs("none"),()=>{t.style.transform=function(t){let e="",n=!0;for(let s=0;s{const n=e.latest.originX??"50%",s=e.latest.originY??"50%",r=e.latest.originZ??0;t.style.transformOrigin=`${n} ${s} ${r}`}),i=e.get("transformOrigin")):r=on(n)?()=>{t.style.setProperty(n,e.latest[n])}:()=>{t.style[n]=e.latest[n]},e.set(n,s,r,i)},ws=os(us(vs)),bs=mt.transform;const Ts=os(us((t,e,n,s)=>{if(n.startsWith("path"))return function(t,e,n,s){return $.render(()=>t.setAttribute("pathLength","1")),"pathOffset"===n?e.set(n,s,()=>t.setAttribute("stroke-dashoffset",bs(-e.latest[n]))):(e.get("stroke-dasharray")||e.set("stroke-dasharray",new fs("1 1"),()=>{const{pathLength:n=1,pathSpacing:s}=e.latest;t.setAttribute("stroke-dasharray",`${bs(n)} ${bs(s??1-Number(n))}`)}),e.set(n,s,void 0,e.get("stroke-dasharray")))}(t,e,n,s);if(n.startsWith("attr"))return cs(t,e,function(t){return t.replace(/^attr([A-Z])/,(t,e)=>e.toLowerCase())}(n),s);return(n in t.style?vs:cs)(t,e,n,s)}));const{schedule:xs,cancel:Vs}=K(queueMicrotask,!1),Ms={x:!1,y:!1};function Ss(){return Ms.x||Ms.y}function As(t,e){const n=is(t),s=new AbortController;return[n,{passive:!0,...e,signal:s.signal},()=>s.abort()]}function ks(t){return!("touch"===t.pointerType||Ss())}const Es=(t,e)=>!!e&&(t===e||Es(t,e.parentElement)),Ps=t=>"mouse"===t.pointerType?"number"!=typeof t.button||t.button<=0:!1!==t.isPrimary,Cs=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);const Os=new WeakSet;function Fs(t){return e=>{"Enter"===e.key&&t(e)}}function Rs(t,e){t.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}function Bs(t){return Ps(t)&&!Ss()}function Ds(t,e){const n=window.getComputedStyle(t);return on(e)?n.getPropertyValue(e):n[e]}function Ls(t){return a(t)&&"ownerSVGElement"in t}const Ws=new WeakMap;let Is;const js=(t,e,n)=>(s,r)=>r&&r[0]?r[0][t+"Size"]:Ls(s)&&"getBBox"in s?s.getBBox()[e]:s[n],Ns=js("inline","width","offsetWidth"),Ks=js("block","height","offsetHeight");function $s({target:t,borderBoxSize:e}){Ws.get(t)?.forEach(n=>{n(t,{get width(){return Ns(t,e)},get height(){return Ks(t,e)}})})}function Us(t){t.forEach($s)}function Ys(t,e){Is||"undefined"!=typeof ResizeObserver&&(Is=new ResizeObserver(Us));const n=is(t);return n.forEach(t=>{let n=Ws.get(t);n||(n=new Set,Ws.set(t,n)),n.add(e),Is?.observe(t)}),()=>{n.forEach(t=>{const n=Ws.get(t);n?.delete(e),n?.size||Is?.unobserve(t)})}}const zs=new Set;let Xs;function Hs(t){return zs.add(t),Xs||(Xs=()=>{const t={get width(){return window.innerWidth},get height(){return window.innerHeight}};zs.forEach(e=>e(t))},window.addEventListener("resize",Xs)),()=>{zs.delete(t),zs.size||"function"!=typeof Xs||(window.removeEventListener("resize",Xs),Xs=void 0)}}function qs(t,e){return"function"==typeof t?Hs(t):Ys(t,e)}function Gs(t,e){let n;const s=()=>{const{currentTime:s}=e,r=(null===s?0:s.value)/100;n!==r&&t(r),n=r};return $.preUpdate(s,!0),()=>U(s)}function Zs(){const{value:t}=N;null!==t?(t.frameloop.rate.push(Y.delta),t.animations.mainThread.push(G.mainThread),t.animations.waapi.push(G.waapi),t.animations.layout.push(G.layout)):U(Zs)}function _s(t){return t.reduce((t,e)=>t+e,0)/t.length}function Js(t,e=_s){return 0===t.length?{min:0,max:0,avg:0}:{min:Math.min(...t),max:Math.max(...t),avg:e(t)}}const Qs=t=>Math.round(1e3/t);function tr(){N.value=null,N.addProjectionMetrics=null}function er(){const{value:t}=N;if(!t)throw new Error("Stats are not being measured");tr(),U(Zs);const e={frameloop:{setup:Js(t.frameloop.setup),rate:Js(t.frameloop.rate),read:Js(t.frameloop.read),resolveKeyframes:Js(t.frameloop.resolveKeyframes),preUpdate:Js(t.frameloop.preUpdate),update:Js(t.frameloop.update),preRender:Js(t.frameloop.preRender),render:Js(t.frameloop.render),postRender:Js(t.frameloop.postRender)},animations:{mainThread:Js(t.animations.mainThread),waapi:Js(t.animations.waapi),layout:Js(t.animations.layout)},layoutProjection:{nodes:Js(t.layoutProjection.nodes),calculatedTargetDeltas:Js(t.layoutProjection.calculatedTargetDeltas),calculatedProjections:Js(t.layoutProjection.calculatedProjections)}},{rate:n}=e.frameloop;return n.min=Qs(n.min),n.max=Qs(n.max),n.avg=Qs(n.avg),[n.min,n.max]=[n.max,n.min],e}function nr(t){return Ls(t)&&"svg"===t.tagName}function sr(t,e){if("first"===t)return 0;{const n=e-1;return"last"===t?n:n/2}}function rr(...t){const e=!Array.isArray(t[0]),n=e?0:-1,s=t[0+n],r=be(t[1+n],t[2+n],t[3+n]);return e?r(s):r}function ir(t){const e=[];ps.current=e;const n=t();ps.current=void 0;const s=ms(n);return function(t,e,n){const s=()=>e.set(n()),r=()=>$.preRender(s,!1,!0),i=t.map(t=>t.on("change",r));e.on("destroy",()=>{i.forEach(t=>t()),U(s)})}(e,s,t),s}const or=t=>Boolean(t&&t.getVelocity);function ar(t,e,n){const s=t.get();let r,i=null,o=s;const a="string"==typeof s?s.replace(/[\d.-]/g,""):void 0,l=()=>{i&&(i.stop(),i=null)},u=()=>{l(),i=new Fe({keyframes:[ur(t.get()),ur(o)],velocity:t.getVelocity(),type:"spring",restDelta:.001,restSpeed:.01,...n,onUpdate:r})};let c;return t.attach((e,n)=>(o=e,r=t=>n(lr(t,a)),$.postRender(u),t.get()),l),or(e)&&(c=e.on("change",e=>t.set(lr(e,a))),t.on("destroy",c)),c}function lr(t,e){return e?t+e:t}function ur(t){return"number"==typeof t?t:parseFloat(t)}const cr=[...Nn,bt,Pt],hr=t=>cr.find(jn(t));function dr(t){return"layout"===t?"group":"enter"===t||"new"===t?"new":"exit"===t||"old"===t?"old":"group"}let pr={},fr=null;const mr=(t,e)=>{pr[t]=e},gr=()=>{fr||(fr=document.createElement("style"),fr.id="motion-view");let t="";for(const e in pr){const n=pr[e];t+=`${e} {\n`;for(const[e,s]of Object.entries(n))t+=` ${e}: ${s};\n`;t+="}\n"}fr.textContent=t,document.head.appendChild(fr),pr={}},yr=()=>{fr&&fr.parentElement&&fr.parentElement.removeChild(fr)};function vr(t){const e=t.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/);return e?{layer:e[2],type:e[1]}:null}function wr(t){const{effect:e}=t;return!!e&&(e.target===document.documentElement&&e.pseudoElement?.startsWith("::view-transition"))}const br=["layout","enter","exit","new","old"];function Tr(t){const{update:e,targets:n,options:s}=t;if(!document.startViewTransition)return new Promise(async t=>{await e(),t(new En([]))});(function(t,e){return e.has(t)&&Object.keys(e.get(t)).length>0})("root",n)||mr(":root",{"view-transition-name":"none"}),mr("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)",{"animation-timing-function":"linear !important"}),gr();const r=document.startViewTransition(async()=>{await e()});return r.finished.finally(()=>{yr()}),new Promise(t=>{r.ready.then(()=>{const e=document.getAnimations().filter(wr),r=[];n.forEach((t,e)=>{for(const n of br){if(!t[n])continue;const{keyframes:i,options:o}=t[n];for(let[t,a]of Object.entries(i)){if(!a)continue;const i={...Wn(s,t),...Wn(o,t)},l=dr(n);if("opacity"===t&&!Array.isArray(a)){a=["new"===l?0:1,a]}"function"==typeof i.delay&&(i.delay=i.delay(0,1)),i.duration&&(i.duration=m(i.duration)),i.delay&&(i.delay=m(i.delay));const u=new vn({...i,element:document.documentElement,name:t,pseudoElement:`::view-transition-${l}(${e})`,keyframes:a});r.push(u)}}});for(const t of e){if("finished"===t.playState)continue;const{effect:e}=t;if(!(e&&e instanceof KeyframeEffect))continue;const{pseudoElement:i}=e;if(!i)continue;const o=vr(i);if(!o)continue;const a=n.get(o.layer);if(a)xr(a,"enter")&&xr(a,"exit")&&e.getKeyframes().some(t=>t.mixBlendMode)?r.push(new Cn(t)):t.cancel();else{const n="group"===o.type?"layout":"";let i={...Wn(s,n)};i.duration&&(i.duration=m(i.duration)),i=yn(i);const a=fn(i.ease,i.duration);e.updateTiming({delay:m(i.delay??0),duration:i.duration,easing:a}),r.push(new Cn(t))}}t(new En(r))})})}function xr(t,e){return t?.[e]?.keyframes.opacity}let Vr=[],Mr=null;function Sr(){Mr=null;const[t]=Vr;var e;t&&(n(Vr,e=t),Mr=e,Tr(e).then(t=>{e.notifyReady(t),t.finished.finally(Sr)}))}function Ar(){for(let t=Vr.length-1;t>=0;t--){const e=Vr[t],{interrupt:n}=e.options;if("immediate"===n){const n=Vr.slice(0,t+1).map(t=>t.update),s=Vr.slice(t+1);e.update=()=>{n.forEach(t=>t())},Vr=[e,...s];break}}Mr&&"immediate"!==Vr[0]?.options.interrupt||Sr()}class kr{constructor(t,e={}){var n;this.currentSubject="root",this.targets=new Map,this.notifyReady=c,this.readyPromise=new Promise(t=>{this.notifyReady=t}),this.update=t,this.options={interrupt:"wait",...e},n=this,Vr.push(n),xs.render(Ar)}get(t){return this.currentSubject=t,this}layout(t,e){return this.updateTarget("layout",t,e),this}new(t,e){return this.updateTarget("new",t,e),this}old(t,e){return this.updateTarget("old",t,e),this}enter(t,e){return this.updateTarget("enter",t,e),this}exit(t,e){return this.updateTarget("exit",t,e),this}crossfade(t){return this.updateTarget("enter",{opacity:1},t),this.updateTarget("exit",{opacity:0},t),this}updateTarget(t,e,n={}){const{currentSubject:s,targets:r}=this;r.has(s)||r.set(s,{});r.get(s)[t]={keyframes:e,options:n}}then(t,e){return this.readyPromise.then(t,e)}}const Er=$,Pr=j.reduce((t,e)=>(t[e]=t=>U(t),t),{});function Cr(t){return"object"==typeof t&&!Array.isArray(t)}function Or(t,e,n,s){return"string"==typeof t&&Cr(e)?is(t,n,s):t instanceof NodeList?Array.from(t):Array.isArray(t)?t:[t]}function Fr(t,e,n){return t*(e+1)}function Rr(t,e,n,s){return"number"==typeof e?e:e.startsWith("-")||e.startsWith("+")?Math.max(0,t+parseFloat(e)):"<"===e?n:e.startsWith("<")?Math.max(0,n+parseFloat(e.slice(1))):s.get(e)??t}function Br(t,e,s,r,i,o){!function(t,e,s){for(let r=0;re&&i.at"number"==typeof t,$r=t=>t.every(Kr),Ur=new WeakMap;function Yr(t){const e=[{},{}];return t?.values.forEach((t,n)=>{e[0][n]=t.get(),e[1][n]=t.getVelocity()}),e}function zr(t,e,n,s){if("function"==typeof e){const[r,i]=Yr(s);e=e(void 0!==n?n:t.custom,r,i)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[r,i]=Yr(s);e=e(void 0!==n?n:t.custom,r,i)}return e}function Xr(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,ms(n))}function Hr(t){return(t=>Array.isArray(t))(t)?t[t.length-1]||0:t}function qr(t,e){const n=function(t,e,n){const s=t.getProps();return zr(s,e,void 0!==n?n:s.custom,t)}(t,e);let{transitionEnd:s={},transition:r={},...i}=n||{};i={...i,...s};for(const e in i){Xr(t,e,Hr(i[e]))}}function Gr(t,e){const n=t.getValue("willChange");if(s=n,Boolean(or(s)&&s.add))return n.add(e);if(!n&&i.WillChange){const n=new i.WillChange("auto");t.addValue("willChange",n),n.add(e)}var s}const Zr=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),_r="data-"+Zr("framerAppearId");function Jr(t){return t.props[_r]}const Qr=t=>null!==t;const ti={type:"spring",stiffness:500,damping:25,restSpeed:10},ei={type:"keyframes",duration:.8},ni={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},si=(t,{keyframes:e})=>e.length>2?ei:Xe.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:ti:ni;const ri=(t,e,n,s={},r,o)=>a=>{const l=Wn(s,t)||{},u=l.delay||s.delay||0;let{elapsed:c=0}=s;c-=m(u);const h={keyframes:Array.isArray(n)?n:[null,n],ease:"easeOut",velocity:e.getVelocity(),...l,delay:-c,onUpdate:t=>{e.set(t),l.onUpdate&&l.onUpdate(t)},onComplete:()=>{a(),l.onComplete&&l.onComplete()},name:t,motionValue:e,element:o?void 0:r};(function({when:t,delay:e,delayChildren:n,staggerChildren:s,staggerDirection:r,repeat:i,repeatType:o,repeatDelay:a,from:l,elapsed:u,...c}){return!!Object.keys(c).length})(l)||Object.assign(h,si(t,h)),h.duration&&(h.duration=m(h.duration)),h.repeatDelay&&(h.repeatDelay=m(h.repeatDelay)),void 0!==h.from&&(h.keyframes[0]=h.from);let d=!1;if((!1===h.type||0===h.duration&&!h.repeatDelay)&&(h.duration=0,0===h.delay&&(d=!0)),(i.instantAnimations||i.skipAnimations)&&(d=!0,h.duration=0,h.delay=0),h.allowFlatten=!l.type&&!l.ease,d&&!o&&void 0!==e.get()){const t=function(t,{repeat:e,repeatType:n="loop"},s){const r=t.filter(Qr),i=e&&"loop"!==n&&e%2==1?0:r.length-1;return i&&void 0!==s?s:r[i]}(h.keyframes,l);if(void 0!==t)return void $.update(()=>{h.onUpdate(t),h.onComplete()})}return l.isSync?new Fe(h):new kn(h)};function ii({protectedKeys:t,needsAnimating:e},n){const s=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,s}function oi(t,e,{delay:n=0,transitionOverride:s,type:r}={}){let{transition:i=t.getDefaultTransition(),transitionEnd:o,...a}=e;s&&(i=s);const l=[],u=r&&t.animationState&&t.animationState.getState()[r];for(const e in a){const s=t.getValue(e,t.latestValues[e]??null),r=a[e];if(void 0===r||u&&ii(u,e))continue;const o={delay:n,...Wn(i||{},e)},c=s.get();if(void 0!==c&&!s.isAnimating&&!Array.isArray(r)&&r===c&&!o.velocity)continue;let h=!1;if(window.MotionHandoffAnimation){const n=Jr(t);if(n){const t=window.MotionHandoffAnimation(n,e,$);null!==t&&(o.startTime=t,h=!0)}}Gr(t,e),s.start(ri(e,s,r,t.shouldReduceMotion&&In.has(e)?{type:!1}:o,t,h));const d=s.animation;d&&l.push(d)}return o&&Promise.all(l).then(()=>{$.update(()=>{o&&qr(t,o)})}),l}const ai={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},li={};for(const t in ai)li[t]={isEnabled:e=>ai[t].some(t=>!!e[t])};const ui=()=>({x:{min:0,max:0},y:{min:0,max:0}}),ci="undefined"!=typeof window,hi={current:null},di={current:!1};const pi=["initial","animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"];function fi(t){return null!==(e=t.animate)&&"object"==typeof e&&"function"==typeof e.start||pi.some(e=>function(t){return"string"==typeof t||Array.isArray(t)}(t[e]));var e}const mi=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class gi{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:s,blockInitialAnimation:r,visualState:i},o={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=rn,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=q.now();this.renderScheduledAtthis.bindToMotionValue(e,t)),di.current||function(){if(di.current=!0,ci)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>hi.current=t.matches;t.addEventListener("change",e),e()}else hi.current=!1}(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||hi.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){this.projection&&this.projection.unmount(),U(this.notifyUpdate),U(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=Xe.has(t);n&&this.onBindTransform&&this.onBindTransform();const s=e.on("change",e=>{this.latestValues[t]=e,this.props.onUpdate&&$.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0)}),r=e.on("renderRequest",this.scheduleRender);let i;window.MotionCheckAppearSync&&(i=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{s(),r(),i&&i(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in li){const e=li[t];if(!e)continue;const{isEnabled:n,Feature:s}=e;if(!this.features[t]&&s&&n(this.props)&&(this.features[t]=new s(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=ms(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){let n=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];return null!=n&&("string"==typeof n&&(o(n)||l(n))?n=parseFloat(n):!hr(n)&&Pt.test(e)&&(n=Jn(t,e)),this.setBaseTarget(t,or(n)?n.get():n)),or(n)?n.get():n}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){const{initial:e}=this.props;let n;if("string"==typeof e||"object"==typeof e){const s=zr(this.props,e,this.presenceContext?.custom);s&&(n=s[t])}if(e&&void 0!==n)return n;const s=this.getBaseTargetFromProps(this.props,t);return void 0===s||or(s)?void 0!==this.initialValues[t]&&void 0===n?void 0:this.baseTarget[t]:s}on(t,e){return this.events[t]||(this.events[t]=new f),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class yi extends gi{constructor(){super(...arguments),this.KeyframeResolver=ts}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;or(t)&&(this.childSubscription=t.on("change",t=>{this.current&&(this.current.textContent=`${t}`)}))}}const vi={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},wi=ze.length;function bi(t,e,n){const{style:s,vars:r,transformOrigin:i}=t;let o=!1,a=!1;for(const t in e){const n=e[t];if(Xe.has(t))o=!0;else if(_(t))r[t]=n;else{const e=as(n,Gn[t]);t.startsWith("origin")?(a=!0,i[t]=e):s[t]=e}}if(e.transform||(o||n?s.transform=function(t,e,n){let s="",r=!0;for(let i=0;i{const l=jr(t),{delay:u=0,times:c=xe(l),type:p="keyframes",repeat:f,repeatType:g,repeatDelay:v=0,...w}=n;let{ease:b=e.ease||"easeOut",duration:T}=n;const x="function"==typeof u?u(o,a):u,V=l.length,M=gn(p)?p:r?.[p||"keyframes"];if(V<=2&&M){let t=100;if(2===V&&$r(l)){const e=l[1]-l[0];t=Math.abs(e)}const e={...w};void 0!==T&&(e.duration=m(T));const n=Zt(e,t,M);b=n.ease,T=n.duration}T??(T=i);const S=h+x;1===c.length&&0===c[0]&&(c[1]=1);const A=c.length-l.length;if(A>0&&Te(c,A),1===l.length&&l.unshift(null),f){T=Fr(T,f);const t=[...l],e=[...c];b=Array.isArray(b)?[...b]:[b];const n=[...b];for(let s=0;s{for(const r in t){const i=t[r];i.sort(Lr);const a=[],l=[],u=[];for(let t=0;t{s.push(...Bi(n,t,e))}),s}function Li(t){return function(e,s,r){let i=[];var o;o=e,i=Array.isArray(o)&&o.some(Array.isArray)?Di(e,s,t):Bi(e,s,r,t);const a=new Pn(i);return t&&(t.animations.push(a),a.finished.then(()=>{n(t.animations,a)})),a}}const Wi=Li();const Ii=t=>function(e,n,s){return new Pn(function(t,e,n,s){const r=is(t,s),i=r.length,o=[];for(let t=0;te.delete(n)),a.push(r)}return a}(e,n,s,t))},ji=Ii(),Ni={x:{length:"Width",position:"Left"},y:{length:"Height",position:"Top"}};function Ki(t,e,n,s){const r=n[e],{length:i,position:o}=Ni[e],a=r.current,l=n.time;r.current=t[`scroll${o}`],r.scrollLength=t[`scroll${i}`]-t[`client${i}`],r.offset.length=0,r.offset[0]=0,r.offset[1]=r.scrollLength,r.progress=p(0,r.scrollLength,r.current);const u=s-l;r.velocity=u>50?0:y(r.current-a,u)}const $i={start:0,center:.5,end:1};function Ui(t,e,n=0){let s=0;if(t in $i&&(t=$i[t]),"string"==typeof t){const e=parseFloat(t);t.endsWith("px")?s=e:t.endsWith("%")?t=e/100:t.endsWith("vw")?s=e/100*document.documentElement.clientWidth:t.endsWith("vh")?s=e/100*document.documentElement.clientHeight:t=e}return"number"==typeof t&&(s=e*t),n+s}const Yi=[0,0];function zi(t,e,n,s){let r=Array.isArray(t)?t:Yi,i=0,o=0;return"number"==typeof t?r=[t,t]:"string"==typeof t&&(r=(t=t.trim()).includes(" ")?t.split(" "):[t,$i[t]?t:"0"]),i=Ui(r[0],n,s),o=Ui(r[1],e),i-o}const Xi={Enter:[[0,1],[1,1]],Exit:[[0,0],[1,0]],Any:[[1,0],[0,1]],All:[[0,0],[1,1]]},Hi={x:0,y:0};function qi(t,e,n){const{offset:r=Xi.All}=n,{target:i=t,axis:o="y"}=n,a="y"===o?"height":"width",l=i!==t?function(t,e){const n={x:0,y:0};let s=t;for(;s&&s!==e;)if(Vn(s))n.x+=s.offsetLeft,n.y+=s.offsetTop,s=s.offsetParent;else if("svg"===s.tagName){const t=s.getBoundingClientRect();s=s.parentElement;const e=s.getBoundingClientRect();n.x+=t.left-e.left,n.y+=t.top-e.top}else{if(!(s instanceof SVGGraphicsElement))break;{const{x:t,y:e}=s.getBBox();n.x+=t,n.y+=e;let r=null,i=s.parentNode;for(;!r;)"svg"===i.tagName&&(r=i),i=s.parentNode;s=r}}return n}(i,t):Hi,u=i===t?{width:t.scrollWidth,height:t.scrollHeight}:function(t){return"getBBox"in t&&"svg"!==t.tagName?t.getBBox():{width:t.clientWidth,height:t.clientHeight}}(i),c={width:t.clientWidth,height:t.clientHeight};e[o].offset.length=0;let h=!e[o].interpolate;const d=r.length;for(let t=0;t{!function(t,e=t,n){if(n.x.targetOffset=0,n.y.targetOffset=0,e!==t){let s=e;for(;s&&s!==t;)n.x.targetOffset+=s.offsetLeft,n.y.targetOffset+=s.offsetTop,s=s.offsetParent}n.x.targetLength=e===t?e.scrollWidth:e.clientWidth,n.y.targetLength=e===t?e.scrollHeight:e.clientHeight,n.x.containerLength=t.clientWidth,n.y.containerLength=t.clientHeight}(t,s.target,n),function(t,e,n){Ki(t,"x",e,n),Ki(t,"y",e,n),e.time=n}(t,n,e),(s.offset||s.target)&&qi(t,n,s)},notify:()=>e(n)}}const Zi=new WeakMap,_i=new WeakMap,Ji=new WeakMap,Qi=t=>t===document.scrollingElement?window:t;function to(t,{container:e=document.scrollingElement,...n}={}){if(!e)return c;let s=Ji.get(e);s||(s=new Set,Ji.set(e,s));const r=Gi(e,t,{time:0,x:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0},y:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0}},n);if(s.add(r),!Zi.has(e)){const t=()=>{for(const t of s)t.measure(Y.timestamp);$.preUpdate(n)},n=()=>{for(const t of s)t.notify()},r=()=>$.read(t);Zi.set(e,r);const i=Qi(e);window.addEventListener("resize",r,{passive:!0}),e!==document.documentElement&&_i.set(e,qs(e,r)),i.addEventListener("scroll",r,{passive:!0}),r()}const i=Zi.get(e);return $.read(i,!1,!0),()=>{U(i);const t=Ji.get(e);if(!t)return;if(t.delete(r),t.size)return;const n=Zi.get(e);Zi.delete(e),n&&(Qi(e).removeEventListener("scroll",n),_i.get(e)?.(),window.removeEventListener("resize",n))}}const eo=new Map;function no({source:t,container:e,...n}){const{axis:s}=n;t&&(e=t);const r=eo.get(e)??new Map;eo.set(e,r);const i=n.target??"self",o=r.get(i)??{},a=s+(n.offset??[]).join(",");return o[a]||(o[a]=!n.target&&ln()?new ScrollTimeline({source:e,axis:s}):function(t){const e={value:0},n=to(n=>{e.value=100*n[t.axis].progress},t);return{currentTime:e,cancel:n}}({container:e,...n})),o[a]}const so={some:0,all:1};const ro=(t,e)=>Math.abs(t-e);t.AsyncMotionValueAnimation=kn,t.DOMKeyframesResolver=ts,t.GroupAnimation=En,t.GroupAnimationWithThen=Pn,t.JSAnimation=Fe,t.KeyframeResolver=rn,t.MotionGlobalConfig=i,t.MotionValue=fs,t.NativeAnimation=vn,t.NativeAnimationExtended=Tn,t.NativeAnimationWrapper=Cn,t.SubscriptionManager=f,t.ViewTransitionBuilder=kr,t.acceleratedValues=rs,t.activeAnimations=G,t.addAttrValue=cs,t.addStyleValue=vs,t.addUniqueItem=e,t.alpha=nt,t.analyseComplexValue=St,t.animate=Wi,t.animateMini=ji,t.animateValue=function(t){return new Fe(t)},t.animateView=function(t,e={}){return new kr(t,e)},t.animationMapKey=Fn,t.anticipate=k,t.applyPxDefaults=ns,t.attachSpring=ar,t.attrEffect=hs,t.backIn=S,t.backInOut=A,t.backOut=M,t.calcGeneratorDuration=Gt,t.cancelFrame=U,t.cancelMicrotask=Vs,t.cancelSync=Pr,t.circIn=E,t.circInOut=C,t.circOut=P,t.clamp=s,t.collectMotionValues=ps,t.color=bt,t.complex=Pt,t.convertOffsetToTimes=Ve,t.createGeneratorEasing=Zt,t.createRenderBatcher=K,t.createScopedAnimate=Li,t.cubicBezier=T,t.cubicBezierAsString=dn,t.defaultEasing=Me,t.defaultOffset=xe,t.defaultTransformValue=Ke,t.defaultValueTypes=Zn,t.degrees=pt,t.delay=function(t,e){return function(t,e){const n=q.now(),s=({timestamp:r})=>{const i=r-n;i>=e&&(U(s),t(i-e))};return $.setup(s,!0),()=>U(s)}(t,m(e))},t.dimensionValueTypes=Nn,t.distance=ro,t.distance2D=function(t,e){const n=ro(t.x,e.x),s=ro(t.y,e.y);return Math.sqrt(n**2+s**2)},t.easeIn=O,t.easeInOut=R,t.easeOut=F,t.easingDefinitionToFunction=I,t.fillOffset=Te,t.fillWildcards=Re,t.findDimensionValueType=Kn,t.findValueType=hr,t.flushKeyframeResolvers=sn,t.frame=$,t.frameData=Y,t.frameSteps=z,t.generateLinearEasing=Ht,t.getAnimatableNone=Jn,t.getAnimationMap=Rn,t.getComputedStyle=Ds,t.getDefaultValueType=_n,t.getEasingForSegment=D,t.getMixer=Kt,t.getOriginIndex=sr,t.getValueAsType=as,t.getValueTransition=Wn,t.getVariableValue=Ln,t.hasWarned=function(t){return v.has(t)},t.hex=ht,t.hover=function(t,e,n={}){const[s,r,i]=As(t,n),o=t=>{if(!ks(t))return;const{target:n}=t,s=e(n,t);if("function"!=typeof s||!n)return;const i=t=>{ks(t)&&(s(t),n.removeEventListener("pointerleave",i))};n.addEventListener("pointerleave",i,r)};return s.forEach(t=>{t.addEventListener("pointerenter",o,r)}),i},t.hsla=wt,t.hslaToRgba=Ot,t.inView=function(t,e,{root:n,margin:s,amount:r="some"}={}){const i=is(t),o=new WeakMap,a=new IntersectionObserver(t=>{t.forEach(t=>{const n=o.get(t.target);if(t.isIntersecting!==Boolean(n))if(t.isIntersecting){const n=e(t.target,t);"function"==typeof n?o.set(t.target,n):a.unobserve(t.target)}else"function"==typeof n&&(n(t),o.delete(t.target))})},{root:n,rootMargin:s,threshold:"number"==typeof r?r:so[r]});return i.forEach(t=>a.observe(t)),()=>a.disconnect()},t.inertia=we,t.interpolate=be,t.invariant=r,t.invisibleValues=It,t.isBezierDefinition=L,t.isCSSVariableName=_,t.isCSSVariableToken=Q,t.isDragActive=Ss,t.isDragging=Ms,t.isEasingArray=B,t.isGenerator=gn,t.isHTMLElement=Vn,t.isMotionValue=or,t.isNodeOrChild=Es,t.isNumericalString=o,t.isObject=a,t.isPrimaryPointer=Ps,t.isSVGElement=Ls,t.isSVGSVGElement=nr,t.isWaapiSupportedEasing=function t(e){return Boolean("function"==typeof e&&hn()||!e||"string"==typeof e&&(e in pn||hn())||L(e)||Array.isArray(e)&&e.every(t))},t.isZeroValueString=l,t.keyframes=Se,t.mapEasingToNativeEasing=fn,t.mapValue=function(t,e,n,s){const r=rr(e,n,s);return ir(()=>r(t.get()))},t.maxGeneratorDuration=qt,t.memo=u,t.microtask=xs,t.millisecondsToSeconds=g,t.mirrorEasing=x,t.mix=zt,t.mixArray=$t,t.mixColor=Wt,t.mixComplex=Yt,t.mixImmediate=Ft,t.mixLinearColor=Bt,t.mixNumber=Rt,t.mixObject=Ut,t.mixVisibility=jt,t.motionValue=ms,t.moveItem=function([...t],e,n){const s=e<0?t.length+e:e;if(s>=0&&s{const s=t.currentTarget;if(!Bs(t))return;Os.add(s);const i=e(s,t),o=(t,e)=>{window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",l),Os.has(s)&&Os.delete(s),Bs(t)&&"function"==typeof i&&i(t,{success:e})},a=t=>{o(t,s===window||s===document||n.useGlobalTarget||Es(s,t.target))},l=t=>{o(t,!1)};window.addEventListener("pointerup",a,r),window.addEventListener("pointercancel",l,r)};return s.forEach(t=>{var e;(n.useGlobalTarget?window:t).addEventListener("pointerdown",o,r),Vn(t)&&(t.addEventListener("focus",t=>((t,e)=>{const n=t.currentTarget;if(!n)return;const s=Fs(()=>{if(Os.has(n))return;Rs(n,"down");const t=Fs(()=>{Rs(n,"up")});n.addEventListener("keyup",t,e),n.addEventListener("blur",()=>Rs(n,"cancel"),e)});n.addEventListener("keydown",s,e),n.addEventListener("blur",()=>n.removeEventListener("keydown",s),e)})(t,r)),e=t,Cs.has(e.tagName)||-1!==e.tabIndex||t.hasAttribute("tabindex")||(t.tabIndex=0))}),i},t.progress=p,t.progressPercentage=vt,t.propEffect=ds,t.px=mt,t.readTransformValue=Ue,t.recordStats=function(){if(N.value)throw tr(),new Error("Stats are already being measured");const t=N;return t.value={frameloop:{setup:[],rate:[],read:[],resolveKeyframes:[],preUpdate:[],update:[],preRender:[],render:[],postRender:[]},animations:{mainThread:[],waapi:[],layout:[]},layoutProjection:{nodes:[],calculatedTargetDeltas:[],calculatedProjections:[]}},t.addProjectionMetrics=e=>{const{layoutProjection:n}=t.value;n.nodes.push(e.nodes),n.calculatedTargetDeltas.push(e.calculatedTargetDeltas),n.calculatedProjections.push(e.calculatedProjections)},$.postRender(Zs,!0),er},t.removeItem=n,t.resize=qs,t.resolveElements=is,t.reverseEasing=V,t.rgbUnit=ut,t.rgba=ct,t.scale=st,t.scroll=function(t,{axis:e="y",container:n=document.scrollingElement,...s}={}){if(!n)return c;const r={axis:e,container:n,...s};return"function"==typeof t?function(t,e){return function(t){return 2===t.length}(t)?to(n=>{t(n[e.axis].progress,n)},e):Gs(t,no(e))}(t,r):function(t,e){const n=no(e);return t.attachTimeline({timeline:e.target?void 0:n,observe:t=>(t.pause(),Gs(e=>{t.time=t.duration*e},n))})}(t,r)},t.scrollInfo=to,t.secondsToMilliseconds=m,t.setDragLock=function(t){return"x"===t||"y"===t?Ms[t]?null:(Ms[t]=!0,()=>{Ms[t]=!1}):Ms.x||Ms.y?null:(Ms.x=Ms.y=!0,()=>{Ms.x=Ms.y=!1})},t.setStyle=an,t.spring=ve,t.springValue=function(t,e){const n=ms(or(t)?t.get():t);return ar(n,t,e),n},t.stagger=function(t=.1,{startDelay:e=0,from:n=0,ease:s}={}){return(r,i)=>{const o="number"==typeof n?n:sr(n,i),a=Math.abs(o-r);let l=t*a;if(s){const e=i*t;l=I(s)(l/e)*e}return e+l}},t.startWaapiAnimation=mn,t.statsBuffer=N,t.steps=function(t,e="end"){return n=>{const r=(n="end"===e?Math.min(n,.999):Math.max(n,.001))*t,i="end"===e?Math.floor(r):Math.ceil(r);return s(0,1,i/t)}},t.styleEffect=ws,t.supportedWaapiEasing=pn,t.supportsBrowserAnimation=An,t.supportsFlags=un,t.supportsLinearEasing=hn,t.supportsPartialKeyframes=ss,t.supportsScrollTimeline=ln,t.svgEffect=Ts,t.sync=Er,t.testValueType=jn,t.time=q,t.transform=rr,t.transformPropOrder=ze,t.transformProps=Xe,t.transformValue=ir,t.transformValueTypes=qn,t.velocityPerSecond=y,t.vh=gt,t.vw=yt,t.warnOnce=function(t,e,n){t||v.has(e)||(console.warn(e),n&&console.warn(n),v.add(e))},t.warning=()=>{},t.wrap=w}); diff --git a/node_modules/framer-motion/dist/es/animation/animate/index.mjs b/node_modules/framer-motion/dist/es/animation/animate/index.mjs new file mode 100644 index 00000000..1171458f --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animate/index.mjs @@ -0,0 +1,38 @@ +import { GroupAnimationWithThen } from 'motion-dom'; +import { removeItem } from 'motion-utils'; +import { animateSequence } from './sequence.mjs'; +import { animateSubject } from './subject.mjs'; + +function isSequence(value) { + return Array.isArray(value) && value.some(Array.isArray); +} +/** + * Creates an animation function that is optionally scoped + * to a specific element. + */ +function createScopedAnimate(scope) { + /** + * Implementation + */ + function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) { + let animations = []; + if (isSequence(subjectOrSequence)) { + animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope); + } + else { + animations = animateSubject(subjectOrSequence, optionsOrKeyframes, options, scope); + } + const animation = new GroupAnimationWithThen(animations); + if (scope) { + scope.animations.push(animation); + animation.finished.then(() => { + removeItem(scope.animations, animation); + }); + } + return animation; + } + return scopedAnimate; +} +const animate = createScopedAnimate(); + +export { animate, createScopedAnimate }; diff --git a/node_modules/framer-motion/dist/es/animation/animate/resolve-subjects.mjs b/node_modules/framer-motion/dist/es/animation/animate/resolve-subjects.mjs new file mode 100644 index 00000000..cb9c63d6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animate/resolve-subjects.mjs @@ -0,0 +1,19 @@ +import { resolveElements } from 'motion-dom'; +import { isDOMKeyframes } from '../utils/is-dom-keyframes.mjs'; + +function resolveSubjects(subject, keyframes, scope, selectorCache) { + if (typeof subject === "string" && isDOMKeyframes(keyframes)) { + return resolveElements(subject, scope, selectorCache); + } + else if (subject instanceof NodeList) { + return Array.from(subject); + } + else if (Array.isArray(subject)) { + return subject; + } + else { + return [subject]; + } +} + +export { resolveSubjects }; diff --git a/node_modules/framer-motion/dist/es/animation/animate/sequence.mjs b/node_modules/framer-motion/dist/es/animation/animate/sequence.mjs new file mode 100644 index 00000000..52a1f920 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animate/sequence.mjs @@ -0,0 +1,14 @@ +import { spring } from 'motion-dom'; +import { createAnimationsFromSequence } from '../sequence/create.mjs'; +import { animateSubject } from './subject.mjs'; + +function animateSequence(sequence, options, scope) { + const animations = []; + const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring }); + animationDefinitions.forEach(({ keyframes, transition }, subject) => { + animations.push(...animateSubject(subject, keyframes, transition)); + }); + return animations; +} + +export { animateSequence }; diff --git a/node_modules/framer-motion/dist/es/animation/animate/single-value.mjs b/node_modules/framer-motion/dist/es/animation/animate/single-value.mjs new file mode 100644 index 00000000..b92156c4 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animate/single-value.mjs @@ -0,0 +1,10 @@ +import { isMotionValue, motionValue } from 'motion-dom'; +import { animateMotionValue } from '../interfaces/motion-value.mjs'; + +function animateSingleValue(value, keyframes, options) { + const motionValue$1 = isMotionValue(value) ? value : motionValue(value); + motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options)); + return motionValue$1.animation; +} + +export { animateSingleValue }; diff --git a/node_modules/framer-motion/dist/es/animation/animate/subject.mjs b/node_modules/framer-motion/dist/es/animation/animate/subject.mjs new file mode 100644 index 00000000..2794836f --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animate/subject.mjs @@ -0,0 +1,52 @@ +import { isMotionValue } from 'motion-dom'; +import { invariant } from 'motion-utils'; +import { visualElementStore } from '../../render/store.mjs'; +import { animateTarget } from '../interfaces/visual-element-target.mjs'; +import { createDOMVisualElement, createObjectVisualElement } from '../utils/create-visual-element.mjs'; +import { isDOMKeyframes } from '../utils/is-dom-keyframes.mjs'; +import { resolveSubjects } from './resolve-subjects.mjs'; +import { animateSingleValue } from './single-value.mjs'; + +function isSingleValue(subject, keyframes) { + return (isMotionValue(subject) || + typeof subject === "number" || + (typeof subject === "string" && !isDOMKeyframes(keyframes))); +} +/** + * Implementation + */ +function animateSubject(subject, keyframes, options, scope) { + const animations = []; + if (isSingleValue(subject, keyframes)) { + animations.push(animateSingleValue(subject, isDOMKeyframes(keyframes) + ? keyframes.default || keyframes + : keyframes, options ? options.default || options : options)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope); + const numSubjects = subjects.length; + invariant(Boolean(numSubjects), "No valid elements provided."); + for (let i = 0; i < numSubjects; i++) { + const thisSubject = subjects[i]; + const createVisualElement = thisSubject instanceof Element + ? createDOMVisualElement + : createObjectVisualElement; + if (!visualElementStore.has(thisSubject)) { + createVisualElement(thisSubject); + } + const visualElement = visualElementStore.get(thisSubject); + const transition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if ("delay" in transition && + typeof transition.delay === "function") { + transition.delay = transition.delay(i, numSubjects); + } + animations.push(...animateTarget(visualElement, { ...keyframes, transition }, {})); + } + } + return animations; +} + +export { animateSubject }; diff --git a/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-elements.mjs b/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-elements.mjs new file mode 100644 index 00000000..21d00f52 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-elements.mjs @@ -0,0 +1,105 @@ +import { resolveElements, getValueTransition, getAnimationMap, animationMapKey, getComputedStyle, fillWildcards, applyPxDefaults, NativeAnimation } from 'motion-dom'; +import { invariant, secondsToMilliseconds } from 'motion-utils'; + +function animateElements(elementOrSelector, keyframes, options, scope) { + const elements = resolveElements(elementOrSelector, scope); + const numElements = elements.length; + invariant(Boolean(numElements), "No valid element provided."); + /** + * WAAPI doesn't support interrupting animations. + * + * Therefore, starting animations requires a three-step process: + * 1. Stop existing animations (write styles to DOM) + * 2. Resolve keyframes (read styles from DOM) + * 3. Create new animations (write styles to DOM) + * + * The hybrid `animate()` function uses AsyncAnimation to resolve + * keyframes before creating new animations, which removes style + * thrashing. Here, we have much stricter filesize constraints. + * Therefore we do this in a synchronous way that ensures that + * at least within `animate()` calls there is no style thrashing. + * + * In the motion-native-animate-mini-interrupt benchmark this + * was 80% faster than a single loop. + */ + const animationDefinitions = []; + /** + * Step 1: Build options and stop existing animations (write) + */ + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + const elementTransition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if (typeof elementTransition.delay === "function") { + elementTransition.delay = elementTransition.delay(i, numElements); + } + for (const valueName in keyframes) { + let valueKeyframes = keyframes[valueName]; + if (!Array.isArray(valueKeyframes)) { + valueKeyframes = [valueKeyframes]; + } + const valueOptions = { + ...getValueTransition(elementTransition, valueName), + }; + valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay)); + /** + * If there's an existing animation playing on this element then stop it + * before creating a new one. + */ + const map = getAnimationMap(element); + const key = animationMapKey(valueName, valueOptions.pseudoElement || ""); + const currentAnimation = map.get(key); + currentAnimation && currentAnimation.stop(); + animationDefinitions.push({ + map, + key, + unresolvedKeyframes: valueKeyframes, + options: { + ...valueOptions, + element, + name: valueName, + allowFlatten: !elementTransition.type && !elementTransition.ease, + }, + }); + } + } + /** + * Step 2: Resolve keyframes (read) + */ + for (let i = 0; i < animationDefinitions.length; i++) { + const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i]; + const { element, name, pseudoElement } = animationOptions; + if (!pseudoElement && unresolvedKeyframes[0] === null) { + unresolvedKeyframes[0] = getComputedStyle(element, name); + } + fillWildcards(unresolvedKeyframes); + applyPxDefaults(unresolvedKeyframes, name); + /** + * If we only have one keyframe, explicitly read the initial keyframe + * from the computed style. This is to ensure consistency with WAAPI behaviour + * for restarting animations, for instance .play() after finish, when it + * has one vs two keyframes. + */ + if (!pseudoElement && unresolvedKeyframes.length < 2) { + unresolvedKeyframes.unshift(getComputedStyle(element, name)); + } + animationOptions.keyframes = unresolvedKeyframes; + } + /** + * Step 3: Create new animations (write) + */ + const animations = []; + for (let i = 0; i < animationDefinitions.length; i++) { + const { map, key, options: animationOptions } = animationDefinitions[i]; + const animation = new NativeAnimation(animationOptions); + map.set(key, animation); + animation.finished.finally(() => map.delete(key)); + animations.push(animation); + } + return animations; +} + +export { animateElements }; diff --git a/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-sequence.mjs b/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-sequence.mjs new file mode 100644 index 00000000..43f1075b --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-sequence.mjs @@ -0,0 +1,13 @@ +import { GroupAnimationWithThen } from 'motion-dom'; +import { createAnimationsFromSequence } from '../../sequence/create.mjs'; +import { animateElements } from './animate-elements.mjs'; + +function animateSequence(definition, options) { + const animations = []; + createAnimationsFromSequence(definition, options).forEach(({ keyframes, transition }, element) => { + animations.push(...animateElements(element, keyframes, transition)); + }); + return new GroupAnimationWithThen(animations); +} + +export { animateSequence }; diff --git a/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-style.mjs b/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-style.mjs new file mode 100644 index 00000000..72a7b2ce --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animators/waapi/animate-style.mjs @@ -0,0 +1,12 @@ +import { GroupAnimationWithThen } from 'motion-dom'; +import { animateElements } from './animate-elements.mjs'; + +const createScopedWaapiAnimate = (scope) => { + function scopedAnimate(elementOrSelector, keyframes, options) { + return new GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope)); + } + return scopedAnimate; +}; +const animateMini = /*@__PURE__*/ createScopedWaapiAnimate(); + +export { animateMini, createScopedWaapiAnimate }; diff --git a/node_modules/framer-motion/dist/es/animation/animators/waapi/utils/get-final-keyframe.mjs b/node_modules/framer-motion/dist/es/animation/animators/waapi/utils/get-final-keyframe.mjs new file mode 100644 index 00000000..50851610 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/animators/waapi/utils/get-final-keyframe.mjs @@ -0,0 +1,12 @@ +const isNotNull = (value) => value !== null; +function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const index = repeat && repeatType !== "loop" && repeat % 2 === 1 + ? 0 + : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; +} + +export { getFinalKeyframe }; diff --git a/node_modules/framer-motion/dist/es/animation/hooks/animation-controls.mjs b/node_modules/framer-motion/dist/es/animation/hooks/animation-controls.mjs new file mode 100644 index 00000000..7edc5e21 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/hooks/animation-controls.mjs @@ -0,0 +1,80 @@ +import { invariant } from 'motion-utils'; +import { setTarget } from '../../render/utils/setters.mjs'; +import { animateVisualElement } from '../interfaces/visual-element.mjs'; + +function stopAnimation(visualElement) { + visualElement.values.forEach((value) => value.stop()); +} +function setVariants(visualElement, variantLabels) { + const reversedLabels = [...variantLabels].reverse(); + reversedLabels.forEach((key) => { + const variant = visualElement.getVariant(key); + variant && setTarget(visualElement, variant); + if (visualElement.variantChildren) { + visualElement.variantChildren.forEach((child) => { + setVariants(child, variantLabels); + }); + } + }); +} +function setValues(visualElement, definition) { + if (Array.isArray(definition)) { + return setVariants(visualElement, definition); + } + else if (typeof definition === "string") { + return setVariants(visualElement, [definition]); + } + else { + setTarget(visualElement, definition); + } +} +/** + * @public + */ +function animationControls() { + /** + * Track whether the host component has mounted. + */ + let hasMounted = false; + /** + * A collection of linked component animation controls. + */ + const subscribers = new Set(); + const controls = { + subscribe(visualElement) { + subscribers.add(visualElement); + return () => void subscribers.delete(visualElement); + }, + start(definition, transitionOverride) { + invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook."); + const animations = []; + subscribers.forEach((visualElement) => { + animations.push(animateVisualElement(visualElement, definition, { + transitionOverride, + })); + }); + return Promise.all(animations); + }, + set(definition) { + invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook."); + return subscribers.forEach((visualElement) => { + setValues(visualElement, definition); + }); + }, + stop() { + subscribers.forEach((visualElement) => { + stopAnimation(visualElement); + }); + }, + mount() { + hasMounted = true; + return () => { + hasMounted = false; + controls.stop(); + }; + }, + }; + return controls; +} + +export { animationControls, setValues }; diff --git a/node_modules/framer-motion/dist/es/animation/hooks/use-animate-style.mjs b/node_modules/framer-motion/dist/es/animation/hooks/use-animate-style.mjs new file mode 100644 index 00000000..173d266e --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/hooks/use-animate-style.mjs @@ -0,0 +1,17 @@ +import { useConstant } from '../../utils/use-constant.mjs'; +import { useUnmountEffect } from '../../utils/use-unmount-effect.mjs'; +import { createScopedWaapiAnimate } from '../animators/waapi/animate-style.mjs'; + +function useAnimateMini() { + const scope = useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = useConstant(() => createScopedWaapiAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + }); + return [scope, animate]; +} + +export { useAnimateMini }; diff --git a/node_modules/framer-motion/dist/es/animation/hooks/use-animate.mjs b/node_modules/framer-motion/dist/es/animation/hooks/use-animate.mjs new file mode 100644 index 00000000..9963520d --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/hooks/use-animate.mjs @@ -0,0 +1,18 @@ +import { useConstant } from '../../utils/use-constant.mjs'; +import { useUnmountEffect } from '../../utils/use-unmount-effect.mjs'; +import { createScopedAnimate } from '../animate/index.mjs'; + +function useAnimate() { + const scope = useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = useConstant(() => createScopedAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + scope.animations.length = 0; + }); + return [scope, animate]; +} + +export { useAnimate }; diff --git a/node_modules/framer-motion/dist/es/animation/hooks/use-animated-state.mjs b/node_modules/framer-motion/dist/es/animation/hooks/use-animated-state.mjs new file mode 100644 index 00000000..425186fc --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/hooks/use-animated-state.mjs @@ -0,0 +1,64 @@ +import { useState, useLayoutEffect } from 'react'; +import { makeUseVisualState } from '../../motion/utils/use-visual-state.mjs'; +import { createBox } from '../../projection/geometry/models.mjs'; +import { VisualElement } from '../../render/VisualElement.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; +import { animateVisualElement } from '../interfaces/visual-element.mjs'; + +const createObject = () => ({}); +class StateVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.measureInstanceViewportBox = createBox; + } + build() { } + resetTransform() { } + restoreTransform() { } + removeValueFromRenderState() { } + renderInstance() { } + scrapeMotionValuesFromProps() { + return createObject(); + } + getBaseTargetFromProps() { + return undefined; + } + readValueFromInstance(_state, key, options) { + return options.initialState[key] || 0; + } + sortInstanceNodePosition() { + return 0; + } +} +const useVisualState = makeUseVisualState({ + scrapeMotionValuesFromProps: createObject, + createRenderState: createObject, +}); +/** + * This is not an officially supported API and may be removed + * on any version. + */ +function useAnimatedState(initialState) { + const [animationState, setAnimationState] = useState(initialState); + const visualState = useVisualState({}, false); + const element = useConstant(() => { + return new StateVisualElement({ + props: { + onUpdate: (v) => { + setAnimationState({ ...v }); + }, + }, + visualState, + presenceContext: null, + }, { initialState }); + }); + useLayoutEffect(() => { + element.mount({}); + return () => element.unmount(); + }, [element]); + const startAnimation = useConstant(() => (animationDefinition) => { + return animateVisualElement(element, animationDefinition); + }); + return [animationState, startAnimation]; +} + +export { useAnimatedState }; diff --git a/node_modules/framer-motion/dist/es/animation/hooks/use-animation.mjs b/node_modules/framer-motion/dist/es/animation/hooks/use-animation.mjs new file mode 100644 index 00000000..a2716876 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/hooks/use-animation.mjs @@ -0,0 +1,41 @@ +import { useConstant } from '../../utils/use-constant.mjs'; +import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs'; +import { animationControls } from './animation-controls.mjs'; + +/** + * Creates `LegacyAnimationControls`, which can be used to manually start, stop + * and sequence animations on one or more components. + * + * The returned `LegacyAnimationControls` should be passed to the `animate` property + * of the components you want to animate. + * + * These components can then be animated with the `start` method. + * + * ```jsx + * import * as React from 'react' + * import { motion, useAnimation } from 'framer-motion' + * + * export function MyComponent(props) { + * const controls = useAnimation() + * + * controls.start({ + * x: 100, + * transition: { duration: 0.5 }, + * }) + * + * return + * } + * ``` + * + * @returns Animation controller with `start` and `stop` methods + * + * @public + */ +function useAnimationControls() { + const controls = useConstant(animationControls); + useIsomorphicLayoutEffect(controls.mount, []); + return controls; +} +const useAnimation = useAnimationControls; + +export { useAnimation, useAnimationControls }; diff --git a/node_modules/framer-motion/dist/es/animation/interfaces/motion-value.mjs b/node_modules/framer-motion/dist/es/animation/interfaces/motion-value.mjs new file mode 100644 index 00000000..a3b1197b --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/interfaces/motion-value.mjs @@ -0,0 +1,98 @@ +import { getValueTransition, frame, JSAnimation, AsyncMotionValueAnimation } from 'motion-dom'; +import { secondsToMilliseconds, MotionGlobalConfig } from 'motion-utils'; +import { getFinalKeyframe } from '../animators/waapi/utils/get-final-keyframe.mjs'; +import { getDefaultTransition } from '../utils/default-transitions.mjs'; +import { isTransitionDefined } from '../utils/is-transition-defined.mjs'; + +const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => { + const valueTransition = getValueTransition(transition, name) || {}; + /** + * Most transition values are currently completely overwritten by value-specific + * transitions. In the future it'd be nicer to blend these transitions. But for now + * delay actually does inherit from the root transition if not value-specific. + */ + const delay = valueTransition.delay || transition.delay || 0; + /** + * Elapsed isn't a public transition option but can be passed through from + * optimized appear effects in milliseconds. + */ + let { elapsed = 0 } = transition; + elapsed = elapsed - secondsToMilliseconds(delay); + const options = { + keyframes: Array.isArray(target) ? target : [null, target], + ease: "easeOut", + velocity: value.getVelocity(), + ...valueTransition, + delay: -elapsed, + onUpdate: (v) => { + value.set(v); + valueTransition.onUpdate && valueTransition.onUpdate(v); + }, + onComplete: () => { + onComplete(); + valueTransition.onComplete && valueTransition.onComplete(); + }, + name, + motionValue: value, + element: isHandoff ? undefined : element, + }; + /** + * If there's no transition defined for this value, we can generate + * unique transition settings for this value. + */ + if (!isTransitionDefined(valueTransition)) { + Object.assign(options, getDefaultTransition(name, options)); + } + /** + * Both WAAPI and our internal animation functions use durations + * as defined by milliseconds, while our external API defines them + * as seconds. + */ + options.duration && (options.duration = secondsToMilliseconds(options.duration)); + options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay)); + /** + * Support deprecated way to set initial value. Prefer keyframe syntax. + */ + if (options.from !== undefined) { + options.keyframes[0] = options.from; + } + let shouldSkip = false; + if (options.type === false || + (options.duration === 0 && !options.repeatDelay)) { + options.duration = 0; + if (options.delay === 0) { + shouldSkip = true; + } + } + if (MotionGlobalConfig.instantAnimations || + MotionGlobalConfig.skipAnimations) { + shouldSkip = true; + options.duration = 0; + options.delay = 0; + } + /** + * If the transition type or easing has been explicitly set by the user + * then we don't want to allow flattening the animation. + */ + options.allowFlatten = !valueTransition.type && !valueTransition.ease; + /** + * If we can or must skip creating the animation, and apply only + * the final keyframe, do so. We also check once keyframes are resolved but + * this early check prevents the need to create an animation at all. + */ + if (shouldSkip && !isHandoff && value.get() !== undefined) { + const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition); + if (finalKeyframe !== undefined) { + frame.update(() => { + options.onUpdate(finalKeyframe); + options.onComplete(); + }); + return; + } + } + return valueTransition.isSync + ? new JSAnimation(options) + : new AsyncMotionValueAnimation(options); +}; + +export { animateMotionValue }; diff --git a/node_modules/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs b/node_modules/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs new file mode 100644 index 00000000..fc5ac6cb --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs @@ -0,0 +1,83 @@ +import { getValueTransition, frame, positionalKeys } from 'motion-dom'; +import { setTarget } from '../../render/utils/setters.mjs'; +import { addValueToWillChange } from '../../value/use-will-change/add-will-change.mjs'; +import { getOptimisedAppearId } from '../optimized-appear/get-appear-id.mjs'; +import { animateMotionValue } from './motion-value.mjs'; + +/** + * Decide whether we should block this animation. Previously, we achieved this + * just by checking whether the key was listed in protectedKeys, but this + * posed problems if an animation was triggered by afterChildren and protectedKeys + * had been set to true in the meantime. + */ +function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) { + const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true; + needsAnimating[key] = false; + return shouldBlock; +} +function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) { + let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition; + if (transitionOverride) + transition = transitionOverride; + const animations = []; + const animationTypeState = type && + visualElement.animationState && + visualElement.animationState.getState()[type]; + for (const key in target) { + const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null); + const valueTarget = target[key]; + if (valueTarget === undefined || + (animationTypeState && + shouldBlockAnimation(animationTypeState, key))) { + continue; + } + const valueTransition = { + delay, + ...getValueTransition(transition || {}, key), + }; + /** + * If the value is already at the defined target, skip the animation. + */ + const currentValue = value.get(); + if (currentValue !== undefined && + !value.isAnimating && + !Array.isArray(valueTarget) && + valueTarget === currentValue && + !valueTransition.velocity) { + continue; + } + /** + * If this is the first time a value is being animated, check + * to see if we're handling off from an existing animation. + */ + let isHandoff = false; + if (window.MotionHandoffAnimation) { + const appearId = getOptimisedAppearId(visualElement); + if (appearId) { + const startTime = window.MotionHandoffAnimation(appearId, key, frame); + if (startTime !== null) { + valueTransition.startTime = startTime; + isHandoff = true; + } + } + } + addValueToWillChange(visualElement, key); + value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && positionalKeys.has(key) + ? { type: false } + : valueTransition, visualElement, isHandoff)); + const animation = value.animation; + if (animation) { + animations.push(animation); + } + } + if (transitionEnd) { + Promise.all(animations).then(() => { + frame.update(() => { + transitionEnd && setTarget(visualElement, transitionEnd); + }); + }); + } + return animations; +} + +export { animateTarget }; diff --git a/node_modules/framer-motion/dist/es/animation/interfaces/visual-element-variant.mjs b/node_modules/framer-motion/dist/es/animation/interfaces/visual-element-variant.mjs new file mode 100644 index 00000000..7f4042c6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/interfaces/visual-element-variant.mjs @@ -0,0 +1,72 @@ +import { resolveVariant } from '../../render/utils/resolve-dynamic-variants.mjs'; +import { animateTarget } from './visual-element-target.mjs'; + +function animateVariant(visualElement, variant, options = {}) { + const resolved = resolveVariant(visualElement, variant, options.type === "exit" + ? visualElement.presenceContext?.custom + : undefined); + let { transition = visualElement.getDefaultTransition() || {} } = resolved || {}; + if (options.transitionOverride) { + transition = options.transitionOverride; + } + /** + * If we have a variant, create a callback that runs it as an animation. + * Otherwise, we resolve a Promise immediately for a composable no-op. + */ + const getAnimation = resolved + ? () => Promise.all(animateTarget(visualElement, resolved, options)) + : () => Promise.resolve(); + /** + * If we have children, create a callback that runs all their animations. + * Otherwise, we resolve a Promise immediately for a composable no-op. + */ + const getChildAnimations = visualElement.variantChildren && visualElement.variantChildren.size + ? (forwardDelay = 0) => { + const { delayChildren = 0, staggerChildren, staggerDirection, } = transition; + return animateChildren(visualElement, variant, forwardDelay, delayChildren, staggerChildren, staggerDirection, options); + } + : () => Promise.resolve(); + /** + * If the transition explicitly defines a "when" option, we need to resolve either + * this animation or all children animations before playing the other. + */ + const { when } = transition; + if (when) { + const [first, last] = when === "beforeChildren" + ? [getAnimation, getChildAnimations] + : [getChildAnimations, getAnimation]; + return first().then(() => last()); + } + else { + return Promise.all([getAnimation(), getChildAnimations(options.delay)]); + } +} +function animateChildren(visualElement, variant, delay = 0, delayChildren = 0, staggerChildren = 0, staggerDirection = 1, options) { + const animations = []; + const numChildren = visualElement.variantChildren.size; + const maxStaggerDuration = (numChildren - 1) * staggerChildren; + const delayIsFunction = typeof delayChildren === "function"; + const generateStaggerDuration = delayIsFunction + ? (i) => delayChildren(i, numChildren) + : // Support deprecated staggerChildren + staggerDirection === 1 + ? (i = 0) => i * staggerChildren + : (i = 0) => maxStaggerDuration - i * staggerChildren; + Array.from(visualElement.variantChildren) + .sort(sortByTreeOrder) + .forEach((child, i) => { + child.notify("AnimationStart", variant); + animations.push(animateVariant(child, variant, { + ...options, + delay: delay + + (delayIsFunction ? 0 : delayChildren) + + generateStaggerDuration(i), + }).then(() => child.notify("AnimationComplete", variant))); + }); + return Promise.all(animations); +} +function sortByTreeOrder(a, b) { + return a.sortNodePosition(b); +} + +export { animateVariant, sortByTreeOrder }; diff --git a/node_modules/framer-motion/dist/es/animation/interfaces/visual-element.mjs b/node_modules/framer-motion/dist/es/animation/interfaces/visual-element.mjs new file mode 100644 index 00000000..c9f57d1b --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/interfaces/visual-element.mjs @@ -0,0 +1,26 @@ +import { resolveVariant } from '../../render/utils/resolve-dynamic-variants.mjs'; +import { animateTarget } from './visual-element-target.mjs'; +import { animateVariant } from './visual-element-variant.mjs'; + +function animateVisualElement(visualElement, definition, options = {}) { + visualElement.notify("AnimationStart", definition); + let animation; + if (Array.isArray(definition)) { + const animations = definition.map((variant) => animateVariant(visualElement, variant, options)); + animation = Promise.all(animations); + } + else if (typeof definition === "string") { + animation = animateVariant(visualElement, definition, options); + } + else { + const resolvedDefinition = typeof definition === "function" + ? resolveVariant(visualElement, definition, options.custom) + : definition; + animation = Promise.all(animateTarget(visualElement, resolvedDefinition, options)); + } + return animation.then(() => { + visualElement.notify("AnimationComplete", definition); + }); +} + +export { animateVisualElement }; diff --git a/node_modules/framer-motion/dist/es/animation/optimized-appear/data-id.mjs b/node_modules/framer-motion/dist/es/animation/optimized-appear/data-id.mjs new file mode 100644 index 00000000..1714fddb --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/optimized-appear/data-id.mjs @@ -0,0 +1,6 @@ +import { camelToDash } from '../../render/dom/utils/camel-to-dash.mjs'; + +const optimizedAppearDataId = "framerAppearId"; +const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId); + +export { optimizedAppearDataAttribute, optimizedAppearDataId }; diff --git a/node_modules/framer-motion/dist/es/animation/optimized-appear/get-appear-id.mjs b/node_modules/framer-motion/dist/es/animation/optimized-appear/get-appear-id.mjs new file mode 100644 index 00000000..00807307 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/optimized-appear/get-appear-id.mjs @@ -0,0 +1,7 @@ +import { optimizedAppearDataAttribute } from './data-id.mjs'; + +function getOptimisedAppearId(visualElement) { + return visualElement.props[optimizedAppearDataAttribute]; +} + +export { getOptimisedAppearId }; diff --git a/node_modules/framer-motion/dist/es/animation/optimized-appear/handoff.mjs b/node_modules/framer-motion/dist/es/animation/optimized-appear/handoff.mjs new file mode 100644 index 00000000..4d3fecb6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/optimized-appear/handoff.mjs @@ -0,0 +1,38 @@ +import { appearAnimationStore } from './store.mjs'; +import { appearStoreId } from './store-id.mjs'; + +function handoffOptimizedAppearAnimation(elementId, valueName, frame) { + const storeId = appearStoreId(elementId, valueName); + const optimisedAnimation = appearAnimationStore.get(storeId); + if (!optimisedAnimation) { + return null; + } + const { animation, startTime } = optimisedAnimation; + function cancelAnimation() { + window.MotionCancelOptimisedAnimation?.(elementId, valueName, frame); + } + /** + * We can cancel the animation once it's finished now that we've synced + * with Motion. + * + * Prefer onfinish over finished as onfinish is backwards compatible with + * older browsers. + */ + animation.onfinish = cancelAnimation; + if (startTime === null || window.MotionHandoffIsComplete?.(elementId)) { + /** + * If the startTime is null, this animation is the Paint Ready detection animation + * and we can cancel it immediately without handoff. + * + * Or if we've already handed off the animation then we're now interrupting it. + * In which case we need to cancel it. + */ + cancelAnimation(); + return null; + } + else { + return startTime; + } +} + +export { handoffOptimizedAppearAnimation }; diff --git a/node_modules/framer-motion/dist/es/animation/optimized-appear/start.mjs b/node_modules/framer-motion/dist/es/animation/optimized-appear/start.mjs new file mode 100644 index 00000000..1a99e514 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/optimized-appear/start.mjs @@ -0,0 +1,171 @@ +import { startWaapiAnimation } from 'motion-dom'; +import { noop } from 'motion-utils'; +import { optimizedAppearDataId } from './data-id.mjs'; +import { getOptimisedAppearId } from './get-appear-id.mjs'; +import { handoffOptimizedAppearAnimation } from './handoff.mjs'; +import { appearAnimationStore, appearComplete } from './store.mjs'; +import { appearStoreId } from './store-id.mjs'; + +/** + * A single time to use across all animations to manually set startTime + * and ensure they're all in sync. + */ +let startFrameTime; +/** + * A dummy animation to detect when Chrome is ready to start + * painting the page and hold off from triggering the real animation + * until then. We only need one animation to detect paint ready. + * + * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850 + */ +let readyAnimation; +/** + * Keep track of animations that were suspended vs cancelled so we + * can easily resume them when we're done measuring layout. + */ +const suspendedAnimations = new Set(); +function resumeSuspendedAnimations() { + suspendedAnimations.forEach((data) => { + data.animation.play(); + data.animation.startTime = data.startTime; + }); + suspendedAnimations.clear(); +} +function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) { + // Prevent optimised appear animations if Motion has already started animating. + if (window.MotionIsMounted) { + return; + } + const id = element.dataset[optimizedAppearDataId]; + if (!id) + return; + window.MotionHandoffAnimation = handoffOptimizedAppearAnimation; + const storeId = appearStoreId(id, name); + if (!readyAnimation) { + readyAnimation = startWaapiAnimation(element, name, [keyframes[0], keyframes[0]], + /** + * 10 secs is basically just a super-safe duration to give Chrome + * long enough to get the animation ready. + */ + { duration: 10000, ease: "linear" }); + appearAnimationStore.set(storeId, { + animation: readyAnimation, + startTime: null, + }); + /** + * If there's no readyAnimation then there's been no instantiation + * of handoff animations. + */ + window.MotionHandoffAnimation = handoffOptimizedAppearAnimation; + window.MotionHasOptimisedAnimation = (elementId, valueName) => { + if (!elementId) + return false; + /** + * Keep a map of elementIds that have started animating. We check + * via ID instead of Element because of hydration errors and + * pre-hydration checks. We also actively record IDs as they start + * animating rather than simply checking for data-appear-id as + * this attrbute might be present but not lead to an animation, for + * instance if the element's appear animation is on a different + * breakpoint. + */ + if (!valueName) { + return appearComplete.has(elementId); + } + const animationId = appearStoreId(elementId, valueName); + return Boolean(appearAnimationStore.get(animationId)); + }; + window.MotionHandoffMarkAsComplete = (elementId) => { + if (appearComplete.has(elementId)) { + appearComplete.set(elementId, true); + } + }; + window.MotionHandoffIsComplete = (elementId) => { + return appearComplete.get(elementId) === true; + }; + /** + * We only need to cancel transform animations as + * they're the ones that will interfere with the + * layout animation measurements. + */ + window.MotionCancelOptimisedAnimation = (elementId, valueName, frame, canResume) => { + const animationId = appearStoreId(elementId, valueName); + const data = appearAnimationStore.get(animationId); + if (!data) + return; + if (frame && canResume === undefined) { + /** + * Wait until the end of the subsequent frame to cancel the animation + * to ensure we don't remove the animation before the main thread has + * had a chance to resolve keyframes and render. + */ + frame.postRender(() => { + frame.postRender(() => { + data.animation.cancel(); + }); + }); + } + else { + data.animation.cancel(); + } + if (frame && canResume) { + suspendedAnimations.add(data); + frame.render(resumeSuspendedAnimations); + } + else { + appearAnimationStore.delete(animationId); + /** + * If there are no more animations left, we can remove the cancel function. + * This will let us know when we can stop checking for conflicting layout animations. + */ + if (!appearAnimationStore.size) { + window.MotionCancelOptimisedAnimation = undefined; + } + } + }; + window.MotionCheckAppearSync = (visualElement, valueName, value) => { + const appearId = getOptimisedAppearId(visualElement); + if (!appearId) + return; + const valueIsOptimised = window.MotionHasOptimisedAnimation?.(appearId, valueName); + const externalAnimationValue = visualElement.props.values?.[valueName]; + if (!valueIsOptimised || !externalAnimationValue) + return; + const removeSyncCheck = value.on("change", (latestValue) => { + if (externalAnimationValue.get() !== latestValue) { + window.MotionCancelOptimisedAnimation?.(appearId, valueName); + removeSyncCheck(); + } + }); + return removeSyncCheck; + }; + } + const startAnimation = () => { + readyAnimation.cancel(); + const appearAnimation = startWaapiAnimation(element, name, keyframes, options); + /** + * Record the time of the first started animation. We call performance.now() once + * here and once in handoff to ensure we're getting + * close to a frame-locked time. This keeps all animations in sync. + */ + if (startFrameTime === undefined) { + startFrameTime = performance.now(); + } + appearAnimation.startTime = startFrameTime; + appearAnimationStore.set(storeId, { + animation: appearAnimation, + startTime: startFrameTime, + }); + if (onReady) + onReady(appearAnimation); + }; + appearComplete.set(id, false); + if (readyAnimation.ready) { + readyAnimation.ready.then(startAnimation).catch(noop); + } + else { + startAnimation(); + } +} + +export { startOptimizedAppearAnimation }; diff --git a/node_modules/framer-motion/dist/es/animation/optimized-appear/store-id.mjs b/node_modules/framer-motion/dist/es/animation/optimized-appear/store-id.mjs new file mode 100644 index 00000000..86e1e1f1 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/optimized-appear/store-id.mjs @@ -0,0 +1,8 @@ +import { transformProps } from 'motion-dom'; + +const appearStoreId = (elementId, valueName) => { + const key = transformProps.has(valueName) ? "transform" : valueName; + return `${elementId}: ${key}`; +}; + +export { appearStoreId }; diff --git a/node_modules/framer-motion/dist/es/animation/optimized-appear/store.mjs b/node_modules/framer-motion/dist/es/animation/optimized-appear/store.mjs new file mode 100644 index 00000000..5b0d9e8d --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/optimized-appear/store.mjs @@ -0,0 +1,4 @@ +const appearAnimationStore = new Map(); +const appearComplete = new Map(); + +export { appearAnimationStore, appearComplete }; diff --git a/node_modules/framer-motion/dist/es/animation/sequence/create.mjs b/node_modules/framer-motion/dist/es/animation/sequence/create.mjs new file mode 100644 index 00000000..0ea2b01e --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/sequence/create.mjs @@ -0,0 +1,249 @@ +import { isMotionValue, defaultOffset, isGenerator, createGeneratorEasing, fillOffset } from 'motion-dom'; +import { progress, secondsToMilliseconds, invariant, getEasingForSegment } from 'motion-utils'; +import { resolveSubjects } from '../animate/resolve-subjects.mjs'; +import { calculateRepeatDuration } from './utils/calc-repeat-duration.mjs'; +import { calcNextTime } from './utils/calc-time.mjs'; +import { addKeyframes } from './utils/edit.mjs'; +import { normalizeTimes } from './utils/normalize-times.mjs'; +import { compareByTime } from './utils/sort.mjs'; + +const defaultSegmentEasing = "easeInOut"; +const MAX_REPEAT = 20; +function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) { + const defaultDuration = defaultTransition.duration || 0.3; + const animationDefinitions = new Map(); + const sequences = new Map(); + const elementCache = {}; + const timeLabels = new Map(); + let prevTime = 0; + let currentTime = 0; + let totalDuration = 0; + /** + * Build the timeline by mapping over the sequence array and converting + * the definitions into keyframes and offsets with absolute time values. + * These will later get converted into relative offsets in a second pass. + */ + for (let i = 0; i < sequence.length; i++) { + const segment = sequence[i]; + /** + * If this is a timeline label, mark it and skip the rest of this iteration. + */ + if (typeof segment === "string") { + timeLabels.set(segment, currentTime); + continue; + } + else if (!Array.isArray(segment)) { + timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels)); + continue; + } + let [subject, keyframes, transition = {}] = segment; + /** + * If a relative or absolute time value has been specified we need to resolve + * it in relation to the currentTime. + */ + if (transition.at !== undefined) { + currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels); + } + /** + * Keep track of the maximum duration in this definition. This will be + * applied to currentTime once the definition has been parsed. + */ + let maxDuration = 0; + const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => { + const valueKeyframesAsList = keyframesAsList(valueKeyframes); + const { delay = 0, times = defaultOffset(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition; + let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition; + /** + * Resolve stagger() if defined. + */ + const calculatedDelay = typeof delay === "function" + ? delay(elementIndex, numSubjects) + : delay; + /** + * If this animation should and can use a spring, generate a spring easing function. + */ + const numKeyframes = valueKeyframesAsList.length; + const createGenerator = isGenerator(type) + ? type + : generators?.[type || "keyframes"]; + if (numKeyframes <= 2 && createGenerator) { + /** + * As we're creating an easing function from a spring, + * ideally we want to generate it using the real distance + * between the two keyframes. However this isn't always + * possible - in these situations we use 0-100. + */ + let absoluteDelta = 100; + if (numKeyframes === 2 && + isNumberKeyframesArray(valueKeyframesAsList)) { + const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0]; + absoluteDelta = Math.abs(delta); + } + const springTransition = { ...remainingTransition }; + if (duration !== undefined) { + springTransition.duration = secondsToMilliseconds(duration); + } + const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator); + ease = springEasing.ease; + duration = springEasing.duration; + } + duration ?? (duration = defaultDuration); + const startTime = currentTime + calculatedDelay; + /** + * If there's only one time offset of 0, fill in a second with length 1 + */ + if (times.length === 1 && times[0] === 0) { + times[1] = 1; + } + /** + * Fill out if offset if fewer offsets than keyframes + */ + const remainder = times.length - valueKeyframesAsList.length; + remainder > 0 && fillOffset(times, remainder); + /** + * If only one value has been set, ie [1], push a null to the start of + * the keyframe array. This will let us mark a keyframe at this point + * that will later be hydrated with the previous value. + */ + valueKeyframesAsList.length === 1 && + valueKeyframesAsList.unshift(null); + /** + * Handle repeat options + */ + if (repeat) { + invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20"); + duration = calculateRepeatDuration(duration, repeat); + const originalKeyframes = [...valueKeyframesAsList]; + const originalTimes = [...times]; + ease = Array.isArray(ease) ? [...ease] : [ease]; + const originalEase = [...ease]; + for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { + valueKeyframesAsList.push(...originalKeyframes); + for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) { + times.push(originalTimes[keyframeIndex] + (repeatIndex + 1)); + ease.push(keyframeIndex === 0 + ? "linear" + : getEasingForSegment(originalEase, keyframeIndex - 1)); + } + } + normalizeTimes(times, repeat); + } + const targetTime = startTime + duration; + /** + * Add keyframes, mapping offsets to absolute time. + */ + addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime); + maxDuration = Math.max(calculatedDelay + duration, maxDuration); + totalDuration = Math.max(targetTime, totalDuration); + }; + if (isMotionValue(subject)) { + const subjectSequence = getSubjectSequence(subject, sequences); + resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope, elementCache); + const numSubjects = subjects.length; + /** + * For every element in this segment, process the defined values. + */ + for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) { + /** + * Cast necessary, but we know these are of this type + */ + keyframes = keyframes; + transition = transition; + const thisSubject = subjects[subjectIndex]; + const subjectSequence = getSubjectSequence(thisSubject, sequences); + for (const key in keyframes) { + resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects); + } + } + } + prevTime = currentTime; + currentTime += maxDuration; + } + /** + * For every element and value combination create a new animation. + */ + sequences.forEach((valueSequences, element) => { + for (const key in valueSequences) { + const valueSequence = valueSequences[key]; + /** + * Arrange all the keyframes in ascending time order. + */ + valueSequence.sort(compareByTime); + const keyframes = []; + const valueOffset = []; + const valueEasing = []; + /** + * For each keyframe, translate absolute times into + * relative offsets based on the total duration of the timeline. + */ + for (let i = 0; i < valueSequence.length; i++) { + const { at, value, easing } = valueSequence[i]; + keyframes.push(value); + valueOffset.push(progress(0, totalDuration, at)); + valueEasing.push(easing || "easeOut"); + } + /** + * If the first keyframe doesn't land on offset: 0 + * provide one by duplicating the initial keyframe. This ensures + * it snaps to the first keyframe when the animation starts. + */ + if (valueOffset[0] !== 0) { + valueOffset.unshift(0); + keyframes.unshift(keyframes[0]); + valueEasing.unshift(defaultSegmentEasing); + } + /** + * If the last keyframe doesn't land on offset: 1 + * provide one with a null wildcard value. This will ensure it + * stays static until the end of the animation. + */ + if (valueOffset[valueOffset.length - 1] !== 1) { + valueOffset.push(1); + keyframes.push(null); + } + if (!animationDefinitions.has(element)) { + animationDefinitions.set(element, { + keyframes: {}, + transition: {}, + }); + } + const definition = animationDefinitions.get(element); + definition.keyframes[key] = keyframes; + definition.transition[key] = { + ...defaultTransition, + duration: totalDuration, + ease: valueEasing, + times: valueOffset, + ...sequenceTransition, + }; + } + }); + return animationDefinitions; +} +function getSubjectSequence(subject, sequences) { + !sequences.has(subject) && sequences.set(subject, {}); + return sequences.get(subject); +} +function getValueSequence(name, sequences) { + if (!sequences[name]) + sequences[name] = []; + return sequences[name]; +} +function keyframesAsList(keyframes) { + return Array.isArray(keyframes) ? keyframes : [keyframes]; +} +function getValueTransition(transition, key) { + return transition && transition[key] + ? { + ...transition, + ...transition[key], + } + : { ...transition }; +} +const isNumber = (keyframe) => typeof keyframe === "number"; +const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber); + +export { createAnimationsFromSequence, getValueTransition }; diff --git a/node_modules/framer-motion/dist/es/animation/sequence/utils/calc-repeat-duration.mjs b/node_modules/framer-motion/dist/es/animation/sequence/utils/calc-repeat-duration.mjs new file mode 100644 index 00000000..93372213 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/sequence/utils/calc-repeat-duration.mjs @@ -0,0 +1,5 @@ +function calculateRepeatDuration(duration, repeat, _repeatDelay) { + return duration * (repeat + 1); +} + +export { calculateRepeatDuration }; diff --git a/node_modules/framer-motion/dist/es/animation/sequence/utils/calc-time.mjs b/node_modules/framer-motion/dist/es/animation/sequence/utils/calc-time.mjs new file mode 100644 index 00000000..b558a5ba --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/sequence/utils/calc-time.mjs @@ -0,0 +1,23 @@ +/** + * Given a absolute or relative time definition and current/prev time state of the sequence, + * calculate an absolute time for the next keyframes. + */ +function calcNextTime(current, next, prev, labels) { + if (typeof next === "number") { + return next; + } + else if (next.startsWith("-") || next.startsWith("+")) { + return Math.max(0, current + parseFloat(next)); + } + else if (next === "<") { + return prev; + } + else if (next.startsWith("<")) { + return Math.max(0, prev + parseFloat(next.slice(1))); + } + else { + return labels.get(next) ?? current; + } +} + +export { calcNextTime }; diff --git a/node_modules/framer-motion/dist/es/animation/sequence/utils/edit.mjs b/node_modules/framer-motion/dist/es/animation/sequence/utils/edit.mjs new file mode 100644 index 00000000..aa00b8ed --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/sequence/utils/edit.mjs @@ -0,0 +1,30 @@ +import { mixNumber } from 'motion-dom'; +import { getEasingForSegment, removeItem } from 'motion-utils'; + +function eraseKeyframes(sequence, startTime, endTime) { + for (let i = 0; i < sequence.length; i++) { + const keyframe = sequence[i]; + if (keyframe.at > startTime && keyframe.at < endTime) { + removeItem(sequence, keyframe); + // If we remove this item we have to push the pointer back one + i--; + } + } +} +function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) { + /** + * Erase every existing value between currentTime and targetTime, + * this will essentially splice this timeline into any currently + * defined ones. + */ + eraseKeyframes(sequence, startTime, endTime); + for (let i = 0; i < keyframes.length; i++) { + sequence.push({ + value: keyframes[i], + at: mixNumber(startTime, endTime, offset[i]), + easing: getEasingForSegment(easing, i), + }); + } +} + +export { addKeyframes, eraseKeyframes }; diff --git a/node_modules/framer-motion/dist/es/animation/sequence/utils/normalize-times.mjs b/node_modules/framer-motion/dist/es/animation/sequence/utils/normalize-times.mjs new file mode 100644 index 00000000..ad36448b --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/sequence/utils/normalize-times.mjs @@ -0,0 +1,13 @@ +/** + * Take an array of times that represent repeated keyframes. For instance + * if we have original times of [0, 0.5, 1] then our repeated times will + * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back + * down to a 0-1 scale. + */ +function normalizeTimes(times, repeat) { + for (let i = 0; i < times.length; i++) { + times[i] = times[i] / (repeat + 1); + } +} + +export { normalizeTimes }; diff --git a/node_modules/framer-motion/dist/es/animation/sequence/utils/sort.mjs b/node_modules/framer-motion/dist/es/animation/sequence/utils/sort.mjs new file mode 100644 index 00000000..6b1ea690 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/sequence/utils/sort.mjs @@ -0,0 +1,14 @@ +function compareByTime(a, b) { + if (a.at === b.at) { + if (a.value === null) + return 1; + if (b.value === null) + return -1; + return 0; + } + else { + return a.at - b.at; + } +} + +export { compareByTime }; diff --git a/node_modules/framer-motion/dist/es/animation/utils/create-visual-element.mjs b/node_modules/framer-motion/dist/es/animation/utils/create-visual-element.mjs new file mode 100644 index 00000000..60995cd9 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/utils/create-visual-element.mjs @@ -0,0 +1,44 @@ +import { isSVGElement, isSVGSVGElement } from 'motion-dom'; +import { HTMLVisualElement } from '../../render/html/HTMLVisualElement.mjs'; +import { ObjectVisualElement } from '../../render/object/ObjectVisualElement.mjs'; +import { visualElementStore } from '../../render/store.mjs'; +import { SVGVisualElement } from '../../render/svg/SVGVisualElement.mjs'; + +function createDOMVisualElement(element) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + transform: {}, + transformOrigin: {}, + style: {}, + vars: {}, + attrs: {}, + }, + latestValues: {}, + }, + }; + const node = isSVGElement(element) && !isSVGSVGElement(element) + ? new SVGVisualElement(options) + : new HTMLVisualElement(options); + node.mount(element); + visualElementStore.set(element, node); +} +function createObjectVisualElement(subject) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + output: {}, + }, + latestValues: {}, + }, + }; + const node = new ObjectVisualElement(options); + node.mount(subject); + visualElementStore.set(subject, node); +} + +export { createDOMVisualElement, createObjectVisualElement }; diff --git a/node_modules/framer-motion/dist/es/animation/utils/default-transitions.mjs b/node_modules/framer-motion/dist/es/animation/utils/default-transitions.mjs new file mode 100644 index 00000000..7cadb0f5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/utils/default-transitions.mjs @@ -0,0 +1,40 @@ +import { transformProps } from 'motion-dom'; + +const underDampedSpring = { + type: "spring", + stiffness: 500, + damping: 25, + restSpeed: 10, +}; +const criticallyDampedSpring = (target) => ({ + type: "spring", + stiffness: 550, + damping: target === 0 ? 2 * Math.sqrt(550) : 30, + restSpeed: 10, +}); +const keyframesTransition = { + type: "keyframes", + duration: 0.8, +}; +/** + * Default easing curve is a slightly shallower version of + * the default browser easing curve. + */ +const ease = { + type: "keyframes", + ease: [0.25, 0.1, 0.35, 1], + duration: 0.3, +}; +const getDefaultTransition = (valueKey, { keyframes }) => { + if (keyframes.length > 2) { + return keyframesTransition; + } + else if (transformProps.has(valueKey)) { + return valueKey.startsWith("scale") + ? criticallyDampedSpring(keyframes[1]) + : underDampedSpring; + } + return ease; +}; + +export { getDefaultTransition }; diff --git a/node_modules/framer-motion/dist/es/animation/utils/is-animation-controls.mjs b/node_modules/framer-motion/dist/es/animation/utils/is-animation-controls.mjs new file mode 100644 index 00000000..a59e87eb --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/utils/is-animation-controls.mjs @@ -0,0 +1,7 @@ +function isAnimationControls(v) { + return (v !== null && + typeof v === "object" && + typeof v.start === "function"); +} + +export { isAnimationControls }; diff --git a/node_modules/framer-motion/dist/es/animation/utils/is-dom-keyframes.mjs b/node_modules/framer-motion/dist/es/animation/utils/is-dom-keyframes.mjs new file mode 100644 index 00000000..86dbef7b --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/utils/is-dom-keyframes.mjs @@ -0,0 +1,5 @@ +function isDOMKeyframes(keyframes) { + return typeof keyframes === "object" && !Array.isArray(keyframes); +} + +export { isDOMKeyframes }; diff --git a/node_modules/framer-motion/dist/es/animation/utils/is-keyframes-target.mjs b/node_modules/framer-motion/dist/es/animation/utils/is-keyframes-target.mjs new file mode 100644 index 00000000..3c637bf2 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/utils/is-keyframes-target.mjs @@ -0,0 +1,5 @@ +const isKeyframesTarget = (v) => { + return Array.isArray(v); +}; + +export { isKeyframesTarget }; diff --git a/node_modules/framer-motion/dist/es/animation/utils/is-transition-defined.mjs b/node_modules/framer-motion/dist/es/animation/utils/is-transition-defined.mjs new file mode 100644 index 00000000..0fdcfd51 --- /dev/null +++ b/node_modules/framer-motion/dist/es/animation/utils/is-transition-defined.mjs @@ -0,0 +1,10 @@ +/** + * Decide whether a transition is defined on a given Transition. + * This filters out orchestration options and returns true + * if any options are left. + */ +function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) { + return !!Object.keys(transition).length; +} + +export { isTransitionDefined }; diff --git a/node_modules/framer-motion/dist/es/client.mjs b/node_modules/framer-motion/dist/es/client.mjs new file mode 100644 index 00000000..143aedc8 --- /dev/null +++ b/node_modules/framer-motion/dist/es/client.mjs @@ -0,0 +1,3 @@ +"use client"; +export { MotionA as a, MotionAbbr as abbr, MotionAddress as address, MotionAnimate as animate, MotionArea as area, MotionArticle as article, MotionAside as aside, MotionAudio as audio, MotionB as b, MotionBase as base, MotionBdi as bdi, MotionBdo as bdo, MotionBig as big, MotionBlockquote as blockquote, MotionBody as body, MotionButton as button, MotionCanvas as canvas, MotionCaption as caption, MotionCircle as circle, MotionCite as cite, MotionClipPath as clipPath, MotionCode as code, MotionCol as col, MotionColgroup as colgroup, MotionData as data, MotionDatalist as datalist, MotionDd as dd, MotionDefs as defs, MotionDel as del, MotionDesc as desc, MotionDetails as details, MotionDfn as dfn, MotionDialog as dialog, MotionDiv as div, MotionDl as dl, MotionDt as dt, MotionEllipse as ellipse, MotionEm as em, MotionEmbed as embed, MotionFeBlend as feBlend, MotionFeColorMatrix as feColorMatrix, MotionFeComponentTransfer as feComponentTransfer, MotionFeComposite as feComposite, MotionFeConvolveMatrix as feConvolveMatrix, MotionFeDiffuseLighting as feDiffuseLighting, MotionFeDisplacementMap as feDisplacementMap, MotionFeDistantLight as feDistantLight, MotionFeDropShadow as feDropShadow, MotionFeFlood as feFlood, MotionFeFuncA as feFuncA, MotionFeFuncB as feFuncB, MotionFeFuncG as feFuncG, MotionFeFuncR as feFuncR, MotionFeGaussianBlur as feGaussianBlur, MotionFeImage as feImage, MotionFeMerge as feMerge, MotionFeMergeNode as feMergeNode, MotionFeMorphology as feMorphology, MotionFeOffset as feOffset, MotionFePointLight as fePointLight, MotionFeSpecularLighting as feSpecularLighting, MotionFeSpotLight as feSpotLight, MotionFeTile as feTile, MotionFeTurbulence as feTurbulence, MotionFieldset as fieldset, MotionFigcaption as figcaption, MotionFigure as figure, MotionFilter as filter, MotionFooter as footer, MotionForeignObject as foreignObject, MotionForm as form, MotionG as g, MotionH1 as h1, MotionH2 as h2, MotionH3 as h3, MotionH4 as h4, MotionH5 as h5, MotionH6 as h6, MotionHead as head, MotionHeader as header, MotionHgroup as hgroup, MotionHr as hr, MotionHtml as html, MotionI as i, MotionIframe as iframe, MotionImage as image, MotionImg as img, MotionInput as input, MotionIns as ins, MotionKbd as kbd, MotionKeygen as keygen, MotionLabel as label, MotionLegend as legend, MotionLi as li, MotionLine as line, MotionLinearGradient as linearGradient, MotionLink as link, MotionMain as main, MotionMap as map, MotionMark as mark, MotionMarker as marker, MotionMask as mask, MotionMenu as menu, MotionMenuitem as menuitem, MotionMetadata as metadata, MotionMeter as meter, MotionNav as nav, MotionObject as object, MotionOl as ol, MotionOptgroup as optgroup, MotionOption as option, MotionOutput as output, MotionP as p, MotionParam as param, MotionPath as path, MotionPattern as pattern, MotionPicture as picture, MotionPolygon as polygon, MotionPolyline as polyline, MotionPre as pre, MotionProgress as progress, MotionQ as q, MotionRadialGradient as radialGradient, MotionRect as rect, MotionRp as rp, MotionRt as rt, MotionRuby as ruby, MotionS as s, MotionSamp as samp, MotionScript as script, MotionSection as section, MotionSelect as select, MotionSmall as small, MotionSource as source, MotionSpan as span, MotionStop as stop, MotionStrong as strong, MotionStyle as style, MotionSub as sub, MotionSummary as summary, MotionSup as sup, MotionSvg as svg, MotionSymbol as symbol, MotionTable as table, MotionTbody as tbody, MotionTd as td, MotionText as text, MotionTextPath as textPath, MotionTextarea as textarea, MotionTfoot as tfoot, MotionTh as th, MotionThead as thead, MotionTime as time, MotionTitle as title, MotionTr as tr, MotionTrack as track, MotionTspan as tspan, MotionU as u, MotionUl as ul, MotionUse as use, MotionVideo as video, MotionView as view, MotionWbr as wbr, MotionWebview as webview } from './render/components/motion/elements.mjs'; +export { createMotionComponent as create } from './render/components/motion/create.mjs'; diff --git a/node_modules/framer-motion/dist/es/components/AnimatePresence/PopChild.mjs b/node_modules/framer-motion/dist/es/components/AnimatePresence/PopChild.mjs new file mode 100644 index 00000000..67d4c81e --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimatePresence/PopChild.mjs @@ -0,0 +1,89 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { isHTMLElement } from 'motion-dom'; +import * as React from 'react'; +import { useId, useRef, useContext, useInsertionEffect } from 'react'; +import { MotionConfigContext } from '../../context/MotionConfigContext.mjs'; + +/** + * Measurement functionality has to be within a separate component + * to leverage snapshot lifecycle. + */ +class PopChildMeasure extends React.Component { + getSnapshotBeforeUpdate(prevProps) { + const element = this.props.childRef.current; + if (element && prevProps.isPresent && !this.props.isPresent) { + const parent = element.offsetParent; + const parentWidth = isHTMLElement(parent) + ? parent.offsetWidth || 0 + : 0; + const size = this.props.sizeRef.current; + size.height = element.offsetHeight || 0; + size.width = element.offsetWidth || 0; + size.top = element.offsetTop; + size.left = element.offsetLeft; + size.right = parentWidth - size.width - size.left; + } + return null; + } + /** + * Required with getSnapshotBeforeUpdate to stop React complaining. + */ + componentDidUpdate() { } + render() { + return this.props.children; + } +} +function PopChild({ children, isPresent, anchorX, root }) { + const id = useId(); + const ref = useRef(null); + const size = useRef({ + width: 0, + height: 0, + top: 0, + left: 0, + right: 0, + }); + const { nonce } = useContext(MotionConfigContext); + /** + * We create and inject a style block so we can apply this explicit + * sizing in a non-destructive manner by just deleting the style block. + * + * We can't apply size via render as the measurement happens + * in getSnapshotBeforeUpdate (post-render), likewise if we apply the + * styles directly on the DOM node, we might be overwriting + * styles set via the style prop. + */ + useInsertionEffect(() => { + const { width, height, top, left, right } = size.current; + if (isPresent || !ref.current || !width || !height) + return; + const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`; + ref.current.dataset.motionPopId = id; + const style = document.createElement("style"); + if (nonce) + style.nonce = nonce; + const parent = root ?? document.head; + parent.appendChild(style); + if (style.sheet) { + style.sheet.insertRule(` + [data-motion-pop-id="${id}"] { + position: absolute !important; + width: ${width}px !important; + height: ${height}px !important; + ${x}px !important; + top: ${top}px !important; + } + `); + } + return () => { + parent.removeChild(style); + if (parent.contains(style)) { + parent.removeChild(style); + } + }; + }, [isPresent]); + return (jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React.cloneElement(children, { ref }) })); +} + +export { PopChild }; diff --git a/node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs b/node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs new file mode 100644 index 00000000..938ab9c7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs @@ -0,0 +1,64 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import * as React from 'react'; +import { useId, useMemo } from 'react'; +import { PresenceContext } from '../../context/PresenceContext.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; +import { PopChild } from './PopChild.mjs'; + +const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, root }) => { + const presenceChildren = useConstant(newChildrenMap); + const id = useId(); + let isReusedContext = true; + let context = useMemo(() => { + isReusedContext = false; + return { + id, + initial, + isPresent, + custom, + onExitComplete: (childId) => { + presenceChildren.set(childId, true); + for (const isComplete of presenceChildren.values()) { + if (!isComplete) + return; // can stop searching when any is incomplete + } + onExitComplete && onExitComplete(); + }, + register: (childId) => { + presenceChildren.set(childId, false); + return () => presenceChildren.delete(childId); + }, + }; + }, [isPresent, presenceChildren, onExitComplete]); + /** + * If the presence of a child affects the layout of the components around it, + * we want to make a new context value to ensure they get re-rendered + * so they can detect that layout change. + */ + if (presenceAffectsLayout && isReusedContext) { + context = { ...context }; + } + useMemo(() => { + presenceChildren.forEach((_, key) => presenceChildren.set(key, false)); + }, [isPresent]); + /** + * If there's no `motion` components to fire exit animations, we want to remove this + * component immediately. + */ + React.useEffect(() => { + !isPresent && + !presenceChildren.size && + onExitComplete && + onExitComplete(); + }, [isPresent]); + if (mode === "popLayout") { + children = (jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, root: root, children: children })); + } + return (jsx(PresenceContext.Provider, { value: context, children: children })); +}; +function newChildrenMap() { + return new Map(); +} + +export { PresenceChild }; diff --git a/node_modules/framer-motion/dist/es/components/AnimatePresence/index.mjs b/node_modules/framer-motion/dist/es/components/AnimatePresence/index.mjs new file mode 100644 index 00000000..27040023 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimatePresence/index.mjs @@ -0,0 +1,166 @@ +"use client"; +import { jsx, Fragment } from 'react/jsx-runtime'; +import { useMemo, useRef, useState, useContext } from 'react'; +import { LayoutGroupContext } from '../../context/LayoutGroupContext.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; +import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs'; +import { PresenceChild } from './PresenceChild.mjs'; +import { usePresence } from './use-presence.mjs'; +import { onlyElements, getChildKey } from './utils.mjs'; + +/** + * `AnimatePresence` enables the animation of components that have been removed from the tree. + * + * When adding/removing more than a single child, every child **must** be given a unique `key` prop. + * + * Any `motion` components that have an `exit` property defined will animate out when removed from + * the tree. + * + * ```jsx + * import { motion, AnimatePresence } from 'framer-motion' + * + * export const Items = ({ items }) => ( + * + * {items.map(item => ( + * + * ))} + * + * ) + * ``` + * + * You can sequence exit animations throughout a tree using variants. + * + * If a child contains multiple `motion` components with `exit` props, it will only unmount the child + * once all `motion` components have finished animating out. Likewise, any components using + * `usePresence` all need to call `safeToRemove`. + * + * @public + */ +const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", root }) => { + const [isParentPresent, safeToRemove] = usePresence(propagate); + /** + * Filter any children that aren't ReactElements. We can only track components + * between renders with a props.key. + */ + const presentChildren = useMemo(() => onlyElements(children), [children]); + /** + * Track the keys of the currently rendered children. This is used to + * determine which children are exiting. + */ + const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey); + /** + * If `initial={false}` we only want to pass this to components in the first render. + */ + const isInitialRender = useRef(true); + /** + * A ref containing the currently present children. When all exit animations + * are complete, we use this to re-render the component with the latest children + * *committed* rather than the latest children *rendered*. + */ + const pendingPresentChildren = useRef(presentChildren); + /** + * Track which exiting children have finished animating out. + */ + const exitComplete = useConstant(() => new Map()); + /** + * Save children to render as React state. To ensure this component is concurrent-safe, + * we check for exiting children via an effect. + */ + const [diffedChildren, setDiffedChildren] = useState(presentChildren); + const [renderedChildren, setRenderedChildren] = useState(presentChildren); + useIsomorphicLayoutEffect(() => { + isInitialRender.current = false; + pendingPresentChildren.current = presentChildren; + /** + * Update complete status of exiting children. + */ + for (let i = 0; i < renderedChildren.length; i++) { + const key = getChildKey(renderedChildren[i]); + if (!presentKeys.includes(key)) { + if (exitComplete.get(key) !== true) { + exitComplete.set(key, false); + } + } + else { + exitComplete.delete(key); + } + } + }, [renderedChildren, presentKeys.length, presentKeys.join("-")]); + const exitingChildren = []; + if (presentChildren !== diffedChildren) { + let nextChildren = [...presentChildren]; + /** + * Loop through all the currently rendered components and decide which + * are exiting. + */ + for (let i = 0; i < renderedChildren.length; i++) { + const child = renderedChildren[i]; + const key = getChildKey(child); + if (!presentKeys.includes(key)) { + nextChildren.splice(i, 0, child); + exitingChildren.push(child); + } + } + /** + * If we're in "wait" mode, and we have exiting children, we want to + * only render these until they've all exited. + */ + if (mode === "wait" && exitingChildren.length) { + nextChildren = exitingChildren; + } + setRenderedChildren(onlyElements(nextChildren)); + setDiffedChildren(presentChildren); + /** + * Early return to ensure once we've set state with the latest diffed + * children, we can immediately re-render. + */ + return null; + } + if (process.env.NODE_ENV !== "production" && + mode === "wait" && + renderedChildren.length > 1) { + console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`); + } + /** + * If we've been provided a forceRender function by the LayoutGroupContext, + * we can use it to force a re-render amongst all surrounding components once + * all components have finished animating out. + */ + const { forceRender } = useContext(LayoutGroupContext); + return (jsx(Fragment, { children: renderedChildren.map((child) => { + const key = getChildKey(child); + const isPresent = propagate && !isParentPresent + ? false + : presentChildren === renderedChildren || + presentKeys.includes(key); + const onExit = () => { + if (exitComplete.has(key)) { + exitComplete.set(key, true); + } + else { + return; + } + let isEveryExitComplete = true; + exitComplete.forEach((isExitComplete) => { + if (!isExitComplete) + isEveryExitComplete = false; + }); + if (isEveryExitComplete) { + forceRender?.(); + setRenderedChildren(pendingPresentChildren.current); + propagate && safeToRemove?.(); + onExitComplete && onExitComplete(); + } + }; + return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial + ? undefined + : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, root: root, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key)); + }) })); +}; + +export { AnimatePresence }; diff --git a/node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence-data.mjs b/node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence-data.mjs new file mode 100644 index 00000000..a8a091f0 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence-data.mjs @@ -0,0 +1,9 @@ +import { useContext } from 'react'; +import { PresenceContext } from '../../context/PresenceContext.mjs'; + +function usePresenceData() { + const context = useContext(PresenceContext); + return context ? context.custom : undefined; +} + +export { usePresenceData }; diff --git a/node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence.mjs b/node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence.mjs new file mode 100644 index 00000000..569930a7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimatePresence/use-presence.mjs @@ -0,0 +1,70 @@ +import { useContext, useId, useEffect, useCallback } from 'react'; +import { PresenceContext } from '../../context/PresenceContext.mjs'; + +/** + * When a component is the child of `AnimatePresence`, it can use `usePresence` + * to access information about whether it's still present in the React tree. + * + * ```jsx + * import { usePresence } from "framer-motion" + * + * export const Component = () => { + * const [isPresent, safeToRemove] = usePresence() + * + * useEffect(() => { + * !isPresent && setTimeout(safeToRemove, 1000) + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * If `isPresent` is `false`, it means that a component has been removed the tree, but + * `AnimatePresence` won't really remove it until `safeToRemove` has been called. + * + * @public + */ +function usePresence(subscribe = true) { + const context = useContext(PresenceContext); + if (context === null) + return [true, null]; + const { isPresent, onExitComplete, register } = context; + // It's safe to call the following hooks conditionally (after an early return) because the context will always + // either be null or non-null for the lifespan of the component. + const id = useId(); + useEffect(() => { + if (subscribe) { + return register(id); + } + }, [subscribe]); + const safeToRemove = useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]); + return !isPresent && onExitComplete ? [false, safeToRemove] : [true]; +} +/** + * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present. + * There is no `safeToRemove` function. + * + * ```jsx + * import { useIsPresent } from "framer-motion" + * + * export const Component = () => { + * const isPresent = useIsPresent() + * + * useEffect(() => { + * !isPresent && console.log("I've been removed!") + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * @public + */ +function useIsPresent() { + return isPresent(useContext(PresenceContext)); +} +function isPresent(context) { + return context === null ? true : context.isPresent; +} + +export { isPresent, useIsPresent, usePresence }; diff --git a/node_modules/framer-motion/dist/es/components/AnimatePresence/utils.mjs b/node_modules/framer-motion/dist/es/components/AnimatePresence/utils.mjs new file mode 100644 index 00000000..6819aba4 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimatePresence/utils.mjs @@ -0,0 +1,14 @@ +import { Children, isValidElement } from 'react'; + +const getChildKey = (child) => child.key || ""; +function onlyElements(children) { + const filtered = []; + // We use forEach here instead of map as map mutates the component key by preprending `.$` + Children.forEach(children, (child) => { + if (isValidElement(child)) + filtered.push(child); + }); + return filtered; +} + +export { getChildKey, onlyElements }; diff --git a/node_modules/framer-motion/dist/es/components/AnimateSharedLayout.mjs b/node_modules/framer-motion/dist/es/components/AnimateSharedLayout.mjs new file mode 100644 index 00000000..d8a1e31a --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/AnimateSharedLayout.mjs @@ -0,0 +1,15 @@ +import { jsx } from 'react/jsx-runtime'; +import { invariant } from 'motion-utils'; +import * as React from 'react'; +import { useConstant } from '../utils/use-constant.mjs'; +import { LayoutGroup } from './LayoutGroup/index.mjs'; + +let id = 0; +const AnimateSharedLayout = ({ children }) => { + React.useEffect(() => { + invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations"); + }, []); + return (jsx(LayoutGroup, { id: useConstant(() => `asl-${id++}`), children: children })); +}; + +export { AnimateSharedLayout }; diff --git a/node_modules/framer-motion/dist/es/components/LayoutGroup/index.mjs b/node_modules/framer-motion/dist/es/components/LayoutGroup/index.mjs new file mode 100644 index 00000000..ccd17d76 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/LayoutGroup/index.mjs @@ -0,0 +1,32 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { useContext, useRef, useMemo } from 'react'; +import { LayoutGroupContext } from '../../context/LayoutGroupContext.mjs'; +import { DeprecatedLayoutGroupContext } from '../../context/DeprecatedLayoutGroupContext.mjs'; +import { useForceUpdate } from '../../utils/use-force-update.mjs'; +import { nodeGroup } from '../../projection/node/group.mjs'; + +const shouldInheritGroup = (inherit) => inherit === true; +const shouldInheritId = (inherit) => shouldInheritGroup(inherit === true) || inherit === "id"; +const LayoutGroup = ({ children, id, inherit = true }) => { + const layoutGroupContext = useContext(LayoutGroupContext); + const deprecatedLayoutGroupContext = useContext(DeprecatedLayoutGroupContext); + const [forceRender, key] = useForceUpdate(); + const context = useRef(null); + const upstreamId = layoutGroupContext.id || deprecatedLayoutGroupContext; + if (context.current === null) { + if (shouldInheritId(inherit) && upstreamId) { + id = id ? upstreamId + "-" + id : upstreamId; + } + context.current = { + id, + group: shouldInheritGroup(inherit) + ? layoutGroupContext.group || nodeGroup() + : nodeGroup(), + }; + } + const memoizedContext = useMemo(() => ({ ...context.current, forceRender }), [key]); + return (jsx(LayoutGroupContext.Provider, { value: memoizedContext, children: children })); +}; + +export { LayoutGroup }; diff --git a/node_modules/framer-motion/dist/es/components/LazyMotion/index.mjs b/node_modules/framer-motion/dist/es/components/LazyMotion/index.mjs new file mode 100644 index 00000000..499b7a5a --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/LazyMotion/index.mjs @@ -0,0 +1,68 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { useState, useRef, useEffect } from 'react'; +import { LazyContext } from '../../context/LazyContext.mjs'; +import { loadFeatures } from '../../motion/features/load-features.mjs'; + +/** + * Used in conjunction with the `m` component to reduce bundle size. + * + * `m` is a version of the `motion` component that only loads functionality + * critical for the initial render. + * + * `LazyMotion` can then be used to either synchronously or asynchronously + * load animation and gesture support. + * + * ```jsx + * // Synchronous loading + * import { LazyMotion, m, domAnimation } from "framer-motion" + * + * function App() { + * return ( + * + * + * + * ) + * } + * + * // Asynchronous loading + * import { LazyMotion, m } from "framer-motion" + * + * function App() { + * return ( + * import('./path/to/domAnimation')}> + * + * + * ) + * } + * ``` + * + * @public + */ +function LazyMotion({ children, features, strict = false }) { + const [, setIsLoaded] = useState(!isLazyBundle(features)); + const loadedRenderer = useRef(undefined); + /** + * If this is a synchronous load, load features immediately + */ + if (!isLazyBundle(features)) { + const { renderer, ...loadedFeatures } = features; + loadedRenderer.current = renderer; + loadFeatures(loadedFeatures); + } + useEffect(() => { + if (isLazyBundle(features)) { + features().then(({ renderer, ...loadedFeatures }) => { + loadFeatures(loadedFeatures); + loadedRenderer.current = renderer; + setIsLoaded(true); + }); + } + }, []); + return (jsx(LazyContext.Provider, { value: { renderer: loadedRenderer.current, strict }, children: children })); +} +function isLazyBundle(features) { + return typeof features === "function"; +} + +export { LazyMotion }; diff --git a/node_modules/framer-motion/dist/es/components/MotionConfig/index.mjs b/node_modules/framer-motion/dist/es/components/MotionConfig/index.mjs new file mode 100644 index 00000000..8947fa26 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/MotionConfig/index.mjs @@ -0,0 +1,48 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { useContext, useMemo } from 'react'; +import { MotionConfigContext } from '../../context/MotionConfigContext.mjs'; +import { loadExternalIsValidProp } from '../../render/dom/utils/filter-props.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; + +/** + * `MotionConfig` is used to set configuration options for all children `motion` components. + * + * ```jsx + * import { motion, MotionConfig } from "framer-motion" + * + * export function App() { + * return ( + * + * + * + * ) + * } + * ``` + * + * @public + */ +function MotionConfig({ children, isValidProp, ...config }) { + isValidProp && loadExternalIsValidProp(isValidProp); + /** + * Inherit props from any parent MotionConfig components + */ + config = { ...useContext(MotionConfigContext), ...config }; + /** + * Don't allow isStatic to change between renders as it affects how many hooks + * motion components fire. + */ + config.isStatic = useConstant(() => config.isStatic); + /** + * Creating a new config context object will re-render every `motion` component + * every time it renders. So we only want to create a new one sparingly. + */ + const context = useMemo(() => config, [ + JSON.stringify(config.transition), + config.transformPagePoint, + config.reducedMotion, + ]); + return (jsx(MotionConfigContext.Provider, { value: context, children: children })); +} + +export { MotionConfig }; diff --git a/node_modules/framer-motion/dist/es/components/Reorder/Group.mjs b/node_modules/framer-motion/dist/es/components/Reorder/Group.mjs new file mode 100644 index 00000000..baf025a2 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/Reorder/Group.mjs @@ -0,0 +1,53 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { invariant } from 'motion-utils'; +import { forwardRef, useRef, useEffect } from 'react'; +import { ReorderContext } from '../../context/ReorderContext.mjs'; +import { motion } from '../../render/components/motion/proxy.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; +import { checkReorder } from './utils/check-reorder.mjs'; + +function ReorderGroupComponent({ children, as = "ul", axis = "y", onReorder, values, ...props }, externalRef) { + const Component = useConstant(() => motion[as]); + const order = []; + const isReordering = useRef(false); + invariant(Boolean(values), "Reorder.Group must be provided a values prop"); + const context = { + axis, + registerItem: (value, layout) => { + // If the entry was already added, update it rather than adding it again + const idx = order.findIndex((entry) => value === entry.value); + if (idx !== -1) { + order[idx].layout = layout[axis]; + } + else { + order.push({ value: value, layout: layout[axis] }); + } + order.sort(compareMin); + }, + updateOrder: (item, offset, velocity) => { + if (isReordering.current) + return; + const newOrder = checkReorder(order, item, offset, velocity); + if (order !== newOrder) { + isReordering.current = true; + onReorder(newOrder + .map(getValue) + .filter((value) => values.indexOf(value) !== -1)); + } + }, + }; + useEffect(() => { + isReordering.current = false; + }); + return (jsx(Component, { ...props, ref: externalRef, ignoreStrict: true, children: jsx(ReorderContext.Provider, { value: context, children: children }) })); +} +const ReorderGroup = /*@__PURE__*/ forwardRef(ReorderGroupComponent); +function getValue(item) { + return item.value; +} +function compareMin(a, b) { + return a.layout.min - b.layout.min; +} + +export { ReorderGroup, ReorderGroupComponent }; diff --git a/node_modules/framer-motion/dist/es/components/Reorder/Item.mjs b/node_modules/framer-motion/dist/es/components/Reorder/Item.mjs new file mode 100644 index 00000000..e6671aa9 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/Reorder/Item.mjs @@ -0,0 +1,34 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { isMotionValue } from 'motion-dom'; +import { invariant } from 'motion-utils'; +import { forwardRef, useContext } from 'react'; +import { ReorderContext } from '../../context/ReorderContext.mjs'; +import { motion } from '../../render/components/motion/proxy.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; +import { useMotionValue } from '../../value/use-motion-value.mjs'; +import { useTransform } from '../../value/use-transform.mjs'; + +function useDefaultMotionValue(value, defaultValue = 0) { + return isMotionValue(value) ? value : useMotionValue(defaultValue); +} +function ReorderItemComponent({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) { + const Component = useConstant(() => motion[as]); + const context = useContext(ReorderContext); + const point = { + x: useDefaultMotionValue(style.x), + y: useDefaultMotionValue(style.y), + }; + const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset"); + invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group"); + const { axis, registerItem, updateOrder } = context; + return (jsx(Component, { drag: axis, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDrag: (event, gesturePoint) => { + const { velocity } = gesturePoint; + velocity[axis] && + updateOrder(value, point[axis].get(), velocity[axis]); + onDrag && onDrag(event, gesturePoint); + }, onLayoutMeasure: (measured) => registerItem(value, measured), ref: externalRef, ignoreStrict: true, children: children })); +} +const ReorderItem = /*@__PURE__*/ forwardRef(ReorderItemComponent); + +export { ReorderItem, ReorderItemComponent }; diff --git a/node_modules/framer-motion/dist/es/components/Reorder/namespace.mjs b/node_modules/framer-motion/dist/es/components/Reorder/namespace.mjs new file mode 100644 index 00000000..c0ce0f00 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/Reorder/namespace.mjs @@ -0,0 +1,2 @@ +export { ReorderGroup as Group } from './Group.mjs'; +export { ReorderItem as Item } from './Item.mjs'; diff --git a/node_modules/framer-motion/dist/es/components/Reorder/utils/check-reorder.mjs b/node_modules/framer-motion/dist/es/components/Reorder/utils/check-reorder.mjs new file mode 100644 index 00000000..d6ba9671 --- /dev/null +++ b/node_modules/framer-motion/dist/es/components/Reorder/utils/check-reorder.mjs @@ -0,0 +1,24 @@ +import { mixNumber } from 'motion-dom'; +import { moveItem } from 'motion-utils'; + +function checkReorder(order, value, offset, velocity) { + if (!velocity) + return order; + const index = order.findIndex((item) => item.value === value); + if (index === -1) + return order; + const nextOffset = velocity > 0 ? 1 : -1; + const nextItem = order[index + nextOffset]; + if (!nextItem) + return order; + const item = order[index]; + const nextLayout = nextItem.layout; + const nextItemCenter = mixNumber(nextLayout.min, nextLayout.max, 0.5); + if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) || + (nextOffset === -1 && item.layout.min + offset < nextItemCenter)) { + return moveItem(order, index, index + nextOffset); + } + return order; +} + +export { checkReorder }; diff --git a/node_modules/framer-motion/dist/es/context/DeprecatedLayoutGroupContext.mjs b/node_modules/framer-motion/dist/es/context/DeprecatedLayoutGroupContext.mjs new file mode 100644 index 00000000..16f8bed0 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/DeprecatedLayoutGroupContext.mjs @@ -0,0 +1,10 @@ +import { createContext } from 'react'; + +/** + * Note: Still used by components generated by old versions of Framer + * + * @deprecated + */ +const DeprecatedLayoutGroupContext = createContext(null); + +export { DeprecatedLayoutGroupContext }; diff --git a/node_modules/framer-motion/dist/es/context/LayoutGroupContext.mjs b/node_modules/framer-motion/dist/es/context/LayoutGroupContext.mjs new file mode 100644 index 00000000..0148bacc --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/LayoutGroupContext.mjs @@ -0,0 +1,6 @@ +"use client"; +import { createContext } from 'react'; + +const LayoutGroupContext = createContext({}); + +export { LayoutGroupContext }; diff --git a/node_modules/framer-motion/dist/es/context/LazyContext.mjs b/node_modules/framer-motion/dist/es/context/LazyContext.mjs new file mode 100644 index 00000000..9b04f565 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/LazyContext.mjs @@ -0,0 +1,6 @@ +"use client"; +import { createContext } from 'react'; + +const LazyContext = createContext({ strict: false }); + +export { LazyContext }; diff --git a/node_modules/framer-motion/dist/es/context/MotionConfigContext.mjs b/node_modules/framer-motion/dist/es/context/MotionConfigContext.mjs new file mode 100644 index 00000000..55f28a09 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/MotionConfigContext.mjs @@ -0,0 +1,13 @@ +"use client"; +import { createContext } from 'react'; + +/** + * @public + */ +const MotionConfigContext = createContext({ + transformPagePoint: (p) => p, + isStatic: false, + reducedMotion: "never", +}); + +export { MotionConfigContext }; diff --git a/node_modules/framer-motion/dist/es/context/MotionContext/create.mjs b/node_modules/framer-motion/dist/es/context/MotionContext/create.mjs new file mode 100644 index 00000000..e8fa5734 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/MotionContext/create.mjs @@ -0,0 +1,13 @@ +import { useContext, useMemo } from 'react'; +import { MotionContext } from './index.mjs'; +import { getCurrentTreeVariants } from './utils.mjs'; + +function useCreateMotionContext(props) { + const { initial, animate } = getCurrentTreeVariants(props, useContext(MotionContext)); + return useMemo(() => ({ initial, animate }), [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]); +} +function variantLabelsAsDependency(prop) { + return Array.isArray(prop) ? prop.join(" ") : prop; +} + +export { useCreateMotionContext }; diff --git a/node_modules/framer-motion/dist/es/context/MotionContext/index.mjs b/node_modules/framer-motion/dist/es/context/MotionContext/index.mjs new file mode 100644 index 00000000..cfdec2d7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/MotionContext/index.mjs @@ -0,0 +1,6 @@ +"use client"; +import { createContext } from 'react'; + +const MotionContext = /* @__PURE__ */ createContext({}); + +export { MotionContext }; diff --git a/node_modules/framer-motion/dist/es/context/MotionContext/utils.mjs b/node_modules/framer-motion/dist/es/context/MotionContext/utils.mjs new file mode 100644 index 00000000..3ec9c8ec --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/MotionContext/utils.mjs @@ -0,0 +1,17 @@ +import { isControllingVariants } from '../../render/utils/is-controlling-variants.mjs'; +import { isVariantLabel } from '../../render/utils/is-variant-label.mjs'; + +function getCurrentTreeVariants(props, context) { + if (isControllingVariants(props)) { + const { initial, animate } = props; + return { + initial: initial === false || isVariantLabel(initial) + ? initial + : undefined, + animate: isVariantLabel(animate) ? animate : undefined, + }; + } + return props.inherit !== false ? context : {}; +} + +export { getCurrentTreeVariants }; diff --git a/node_modules/framer-motion/dist/es/context/PresenceContext.mjs b/node_modules/framer-motion/dist/es/context/PresenceContext.mjs new file mode 100644 index 00000000..6b00c1e5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/PresenceContext.mjs @@ -0,0 +1,10 @@ +"use client"; +import { createContext } from 'react'; + +/** + * @public + */ +const PresenceContext = +/* @__PURE__ */ createContext(null); + +export { PresenceContext }; diff --git a/node_modules/framer-motion/dist/es/context/ReorderContext.mjs b/node_modules/framer-motion/dist/es/context/ReorderContext.mjs new file mode 100644 index 00000000..5bc3a57e --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/ReorderContext.mjs @@ -0,0 +1,6 @@ +"use client"; +import { createContext } from 'react'; + +const ReorderContext = createContext(null); + +export { ReorderContext }; diff --git a/node_modules/framer-motion/dist/es/context/SwitchLayoutGroupContext.mjs b/node_modules/framer-motion/dist/es/context/SwitchLayoutGroupContext.mjs new file mode 100644 index 00000000..f2062513 --- /dev/null +++ b/node_modules/framer-motion/dist/es/context/SwitchLayoutGroupContext.mjs @@ -0,0 +1,9 @@ +"use client"; +import { createContext } from 'react'; + +/** + * Internal, exported only for usage in Framer + */ +const SwitchLayoutGroupContext = createContext({}); + +export { SwitchLayoutGroupContext }; diff --git a/node_modules/framer-motion/dist/es/debug.mjs b/node_modules/framer-motion/dist/es/debug.mjs new file mode 100644 index 00000000..75f15e3b --- /dev/null +++ b/node_modules/framer-motion/dist/es/debug.mjs @@ -0,0 +1 @@ +export { recordStats } from 'motion-dom'; diff --git a/node_modules/framer-motion/dist/es/dom-mini.mjs b/node_modules/framer-motion/dist/es/dom-mini.mjs new file mode 100644 index 00000000..bcc1915a --- /dev/null +++ b/node_modules/framer-motion/dist/es/dom-mini.mjs @@ -0,0 +1,2 @@ +export { animateSequence } from './animation/animators/waapi/animate-sequence.mjs'; +export { animateMini as animate } from './animation/animators/waapi/animate-style.mjs'; diff --git a/node_modules/framer-motion/dist/es/dom.mjs b/node_modules/framer-motion/dist/es/dom.mjs new file mode 100644 index 00000000..998703f9 --- /dev/null +++ b/node_modules/framer-motion/dist/es/dom.mjs @@ -0,0 +1,9 @@ +export * from 'motion-dom'; +export * from 'motion-utils'; +export { animate, createScopedAnimate } from './animation/animate/index.mjs'; +export { animateMini } from './animation/animators/waapi/animate-style.mjs'; +export { scroll } from './render/dom/scroll/index.mjs'; +export { scrollInfo } from './render/dom/scroll/track.mjs'; +export { inView } from './render/dom/viewport/index.mjs'; +export { delayInSeconds as delay } from './utils/delay.mjs'; +export { distance, distance2D } from './utils/distance.mjs'; diff --git a/node_modules/framer-motion/dist/es/events/add-dom-event.mjs b/node_modules/framer-motion/dist/es/events/add-dom-event.mjs new file mode 100644 index 00000000..5f0a2fd8 --- /dev/null +++ b/node_modules/framer-motion/dist/es/events/add-dom-event.mjs @@ -0,0 +1,6 @@ +function addDomEvent(target, eventName, handler, options = { passive: true }) { + target.addEventListener(eventName, handler, options); + return () => target.removeEventListener(eventName, handler); +} + +export { addDomEvent }; diff --git a/node_modules/framer-motion/dist/es/events/add-pointer-event.mjs b/node_modules/framer-motion/dist/es/events/add-pointer-event.mjs new file mode 100644 index 00000000..7ebf1ff5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/events/add-pointer-event.mjs @@ -0,0 +1,8 @@ +import { addDomEvent } from './add-dom-event.mjs'; +import { addPointerInfo } from './event-info.mjs'; + +function addPointerEvent(target, eventName, handler, options) { + return addDomEvent(target, eventName, addPointerInfo(handler), options); +} + +export { addPointerEvent }; diff --git a/node_modules/framer-motion/dist/es/events/event-info.mjs b/node_modules/framer-motion/dist/es/events/event-info.mjs new file mode 100644 index 00000000..91e60b53 --- /dev/null +++ b/node_modules/framer-motion/dist/es/events/event-info.mjs @@ -0,0 +1,15 @@ +import { isPrimaryPointer } from 'motion-dom'; + +function extractEventInfo(event) { + return { + point: { + x: event.pageX, + y: event.pageY, + }, + }; +} +const addPointerInfo = (handler) => { + return (event) => isPrimaryPointer(event) && handler(event, extractEventInfo(event)); +}; + +export { addPointerInfo, extractEventInfo }; diff --git a/node_modules/framer-motion/dist/es/events/use-dom-event.mjs b/node_modules/framer-motion/dist/es/events/use-dom-event.mjs new file mode 100644 index 00000000..809d497b --- /dev/null +++ b/node_modules/framer-motion/dist/es/events/use-dom-event.mjs @@ -0,0 +1,34 @@ +import { useEffect } from 'react'; +import { addDomEvent } from './add-dom-event.mjs'; + +/** + * Attaches an event listener directly to the provided DOM element. + * + * Bypassing React's event system can be desirable, for instance when attaching non-passive + * event handlers. + * + * ```jsx + * const ref = useRef(null) + * + * useDomEvent(ref, 'wheel', onWheel, { passive: false }) + * + * return
+ * ``` + * + * @param ref - React.RefObject that's been provided to the element you want to bind the listener to. + * @param eventName - Name of the event you want listen for. + * @param handler - Function to fire when receiving the event. + * @param options - Options to pass to `Event.addEventListener`. + * + * @public + */ +function useDomEvent(ref, eventName, handler, options) { + useEffect(() => { + const element = ref.current; + if (handler && element) { + return addDomEvent(element, eventName, handler, options); + } + }, [ref, eventName, handler, options]); +} + +export { useDomEvent }; diff --git a/node_modules/framer-motion/dist/es/gestures/drag/VisualElementDragControls.mjs b/node_modules/framer-motion/dist/es/gestures/drag/VisualElementDragControls.mjs new file mode 100644 index 00000000..eaa93019 --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/drag/VisualElementDragControls.mjs @@ -0,0 +1,498 @@ +import { frame, mixNumber, setDragLock, percent } from 'motion-dom'; +import { invariant } from 'motion-utils'; +import { animateMotionValue } from '../../animation/interfaces/motion-value.mjs'; +import { addDomEvent } from '../../events/add-dom-event.mjs'; +import { addPointerEvent } from '../../events/add-pointer-event.mjs'; +import { extractEventInfo } from '../../events/event-info.mjs'; +import { convertBoxToBoundingBox, convertBoundingBoxToBox } from '../../projection/geometry/conversion.mjs'; +import { calcLength } from '../../projection/geometry/delta-calc.mjs'; +import { createBox } from '../../projection/geometry/models.mjs'; +import { eachAxis } from '../../projection/utils/each-axis.mjs'; +import { measurePageBox } from '../../projection/utils/measure.mjs'; +import { getContextWindow } from '../../utils/get-context-window.mjs'; +import { isRefObject } from '../../utils/is-ref-object.mjs'; +import { addValueToWillChange } from '../../value/use-will-change/add-will-change.mjs'; +import { PanSession } from '../pan/PanSession.mjs'; +import { applyConstraints, calcRelativeConstraints, resolveDragElastic, rebaseAxisConstraints, calcViewportConstraints, calcOrigin, defaultElastic } from './utils/constraints.mjs'; + +const elementDragControls = new WeakMap(); +class VisualElementDragControls { + constructor(visualElement) { + this.openDragLock = null; + this.isDragging = false; + this.currentDirection = null; + this.originPoint = { x: 0, y: 0 }; + /** + * The permitted boundaries of travel, in pixels. + */ + this.constraints = false; + this.hasMutatedConstraints = false; + /** + * The per-axis resolved elastic values. + */ + this.elastic = createBox(); + /** + * The latest pointer event. Used as fallback when the `cancel` and `stop` functions are called without arguments. + */ + this.latestPointerEvent = null; + /** + * The latest pan info. Used as fallback when the `cancel` and `stop` functions are called without arguments. + */ + this.latestPanInfo = null; + this.visualElement = visualElement; + } + start(originEvent, { snapToCursor = false, distanceThreshold } = {}) { + /** + * Don't start dragging if this component is exiting + */ + const { presenceContext } = this.visualElement; + if (presenceContext && presenceContext.isPresent === false) + return; + const onSessionStart = (event) => { + const { dragSnapToOrigin } = this.getProps(); + // Stop or pause any animations on both axis values immediately. This allows the user to throw and catch + // the component. + dragSnapToOrigin ? this.pauseAnimation() : this.stopAnimation(); + if (snapToCursor) { + this.snapToCursor(extractEventInfo(event).point); + } + }; + const onStart = (event, info) => { + // Attempt to grab the global drag gesture lock - maybe make this part of PanSession + const { drag, dragPropagation, onDragStart } = this.getProps(); + if (drag && !dragPropagation) { + if (this.openDragLock) + this.openDragLock(); + this.openDragLock = setDragLock(drag); + // If we don 't have the lock, don't start dragging + if (!this.openDragLock) + return; + } + this.latestPointerEvent = event; + this.latestPanInfo = info; + this.isDragging = true; + this.currentDirection = null; + this.resolveConstraints(); + if (this.visualElement.projection) { + this.visualElement.projection.isAnimationBlocked = true; + this.visualElement.projection.target = undefined; + } + /** + * Record gesture origin + */ + eachAxis((axis) => { + let current = this.getAxisMotionValue(axis).get() || 0; + /** + * If the MotionValue is a percentage value convert to px + */ + if (percent.test(current)) { + const { projection } = this.visualElement; + if (projection && projection.layout) { + const measuredAxis = projection.layout.layoutBox[axis]; + if (measuredAxis) { + const length = calcLength(measuredAxis); + current = length * (parseFloat(current) / 100); + } + } + } + this.originPoint[axis] = current; + }); + // Fire onDragStart event + if (onDragStart) { + frame.postRender(() => onDragStart(event, info)); + } + addValueToWillChange(this.visualElement, "transform"); + const { animationState } = this.visualElement; + animationState && animationState.setActive("whileDrag", true); + }; + const onMove = (event, info) => { + this.latestPointerEvent = event; + this.latestPanInfo = info; + const { dragPropagation, dragDirectionLock, onDirectionLock, onDrag, } = this.getProps(); + // If we didn't successfully receive the gesture lock, early return. + if (!dragPropagation && !this.openDragLock) + return; + const { offset } = info; + // Attempt to detect drag direction if directionLock is true + if (dragDirectionLock && this.currentDirection === null) { + this.currentDirection = getCurrentDirection(offset); + // If we've successfully set a direction, notify listener + if (this.currentDirection !== null) { + onDirectionLock && onDirectionLock(this.currentDirection); + } + return; + } + // Update each point with the latest position + this.updateAxis("x", info.point, offset); + this.updateAxis("y", info.point, offset); + /** + * Ideally we would leave the renderer to fire naturally at the end of + * this frame but if the element is about to change layout as the result + * of a re-render we want to ensure the browser can read the latest + * bounding box to ensure the pointer and element don't fall out of sync. + */ + this.visualElement.render(); + /** + * This must fire after the render call as it might trigger a state + * change which itself might trigger a layout update. + */ + onDrag && onDrag(event, info); + }; + const onSessionEnd = (event, info) => { + this.latestPointerEvent = event; + this.latestPanInfo = info; + this.stop(event, info); + this.latestPointerEvent = null; + this.latestPanInfo = null; + }; + const resumeAnimation = () => eachAxis((axis) => this.getAnimationState(axis) === "paused" && + this.getAxisMotionValue(axis).animation?.play()); + const { dragSnapToOrigin } = this.getProps(); + this.panSession = new PanSession(originEvent, { + onSessionStart, + onStart, + onMove, + onSessionEnd, + resumeAnimation, + }, { + transformPagePoint: this.visualElement.getTransformPagePoint(), + dragSnapToOrigin, + distanceThreshold, + contextWindow: getContextWindow(this.visualElement), + }); + } + /** + * @internal + */ + stop(event, panInfo) { + const finalEvent = event || this.latestPointerEvent; + const finalPanInfo = panInfo || this.latestPanInfo; + const isDragging = this.isDragging; + this.cancel(); + if (!isDragging || !finalPanInfo || !finalEvent) + return; + const { velocity } = finalPanInfo; + this.startAnimation(velocity); + const { onDragEnd } = this.getProps(); + if (onDragEnd) { + frame.postRender(() => onDragEnd(finalEvent, finalPanInfo)); + } + } + /** + * @internal + */ + cancel() { + this.isDragging = false; + const { projection, animationState } = this.visualElement; + if (projection) { + projection.isAnimationBlocked = false; + } + this.panSession && this.panSession.end(); + this.panSession = undefined; + const { dragPropagation } = this.getProps(); + if (!dragPropagation && this.openDragLock) { + this.openDragLock(); + this.openDragLock = null; + } + animationState && animationState.setActive("whileDrag", false); + } + updateAxis(axis, _point, offset) { + const { drag } = this.getProps(); + // If we're not dragging this axis, do an early return. + if (!offset || !shouldDrag(axis, drag, this.currentDirection)) + return; + const axisValue = this.getAxisMotionValue(axis); + let next = this.originPoint[axis] + offset[axis]; + // Apply constraints + if (this.constraints && this.constraints[axis]) { + next = applyConstraints(next, this.constraints[axis], this.elastic[axis]); + } + axisValue.set(next); + } + resolveConstraints() { + const { dragConstraints, dragElastic } = this.getProps(); + const layout = this.visualElement.projection && + !this.visualElement.projection.layout + ? this.visualElement.projection.measure(false) + : this.visualElement.projection?.layout; + const prevConstraints = this.constraints; + if (dragConstraints && isRefObject(dragConstraints)) { + if (!this.constraints) { + this.constraints = this.resolveRefConstraints(); + } + } + else { + if (dragConstraints && layout) { + this.constraints = calcRelativeConstraints(layout.layoutBox, dragConstraints); + } + else { + this.constraints = false; + } + } + this.elastic = resolveDragElastic(dragElastic); + /** + * If we're outputting to external MotionValues, we want to rebase the measured constraints + * from viewport-relative to component-relative. + */ + if (prevConstraints !== this.constraints && + layout && + this.constraints && + !this.hasMutatedConstraints) { + eachAxis((axis) => { + if (this.constraints !== false && + this.getAxisMotionValue(axis)) { + this.constraints[axis] = rebaseAxisConstraints(layout.layoutBox[axis], this.constraints[axis]); + } + }); + } + } + resolveRefConstraints() { + const { dragConstraints: constraints, onMeasureDragConstraints } = this.getProps(); + if (!constraints || !isRefObject(constraints)) + return false; + const constraintsElement = constraints.current; + invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop."); + const { projection } = this.visualElement; + // TODO + if (!projection || !projection.layout) + return false; + const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint()); + let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox); + /** + * If there's an onMeasureDragConstraints listener we call it and + * if different constraints are returned, set constraints to that + */ + if (onMeasureDragConstraints) { + const userConstraints = onMeasureDragConstraints(convertBoxToBoundingBox(measuredConstraints)); + this.hasMutatedConstraints = !!userConstraints; + if (userConstraints) { + measuredConstraints = convertBoundingBoxToBox(userConstraints); + } + } + return measuredConstraints; + } + startAnimation(velocity) { + const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps(); + const constraints = this.constraints || {}; + const momentumAnimations = eachAxis((axis) => { + if (!shouldDrag(axis, drag, this.currentDirection)) { + return; + } + let transition = (constraints && constraints[axis]) || {}; + if (dragSnapToOrigin) + transition = { min: 0, max: 0 }; + /** + * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame + * of spring animations so we should look into adding a disable spring option to `inertia`. + * We could do something here where we affect the `bounceStiffness` and `bounceDamping` + * using the value of `dragElastic`. + */ + const bounceStiffness = dragElastic ? 200 : 1000000; + const bounceDamping = dragElastic ? 40 : 10000000; + const inertia = { + type: "inertia", + velocity: dragMomentum ? velocity[axis] : 0, + bounceStiffness, + bounceDamping, + timeConstant: 750, + restDelta: 1, + restSpeed: 10, + ...dragTransition, + ...transition, + }; + // If we're not animating on an externally-provided `MotionValue` we can use the + // component's animation controls which will handle interactions with whileHover (etc), + // otherwise we just have to animate the `MotionValue` itself. + return this.startAxisValueAnimation(axis, inertia); + }); + // Run all animations and then resolve the new drag constraints. + return Promise.all(momentumAnimations).then(onDragTransitionEnd); + } + startAxisValueAnimation(axis, transition) { + const axisValue = this.getAxisMotionValue(axis); + addValueToWillChange(this.visualElement, axis); + return axisValue.start(animateMotionValue(axis, axisValue, 0, transition, this.visualElement, false)); + } + stopAnimation() { + eachAxis((axis) => this.getAxisMotionValue(axis).stop()); + } + pauseAnimation() { + eachAxis((axis) => this.getAxisMotionValue(axis).animation?.pause()); + } + getAnimationState(axis) { + return this.getAxisMotionValue(axis).animation?.state; + } + /** + * Drag works differently depending on which props are provided. + * + * - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values. + * - Otherwise, we apply the delta to the x/y motion values. + */ + getAxisMotionValue(axis) { + const dragKey = `_drag${axis.toUpperCase()}`; + const props = this.visualElement.getProps(); + const externalMotionValue = props[dragKey]; + return externalMotionValue + ? externalMotionValue + : this.visualElement.getValue(axis, (props.initial + ? props.initial[axis] + : undefined) || 0); + } + snapToCursor(point) { + eachAxis((axis) => { + const { drag } = this.getProps(); + // If we're not dragging this axis, do an early return. + if (!shouldDrag(axis, drag, this.currentDirection)) + return; + const { projection } = this.visualElement; + const axisValue = this.getAxisMotionValue(axis); + if (projection && projection.layout) { + const { min, max } = projection.layout.layoutBox[axis]; + axisValue.set(point[axis] - mixNumber(min, max, 0.5)); + } + }); + } + /** + * When the viewport resizes we want to check if the measured constraints + * have changed and, if so, reposition the element within those new constraints + * relative to where it was before the resize. + */ + scalePositionWithinConstraints() { + if (!this.visualElement.current) + return; + const { drag, dragConstraints } = this.getProps(); + const { projection } = this.visualElement; + if (!isRefObject(dragConstraints) || !projection || !this.constraints) + return; + /** + * Stop current animations as there can be visual glitching if we try to do + * this mid-animation + */ + this.stopAnimation(); + /** + * Record the relative position of the dragged element relative to the + * constraints box and save as a progress value. + */ + const boxProgress = { x: 0, y: 0 }; + eachAxis((axis) => { + const axisValue = this.getAxisMotionValue(axis); + if (axisValue && this.constraints !== false) { + const latest = axisValue.get(); + boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]); + } + }); + /** + * Update the layout of this element and resolve the latest drag constraints + */ + const { transformTemplate } = this.visualElement.getProps(); + this.visualElement.current.style.transform = transformTemplate + ? transformTemplate({}, "") + : "none"; + projection.root && projection.root.updateScroll(); + projection.updateLayout(); + this.resolveConstraints(); + /** + * For each axis, calculate the current progress of the layout axis + * within the new constraints. + */ + eachAxis((axis) => { + if (!shouldDrag(axis, drag, null)) + return; + /** + * Calculate a new transform based on the previous box progress + */ + const axisValue = this.getAxisMotionValue(axis); + const { min, max } = this.constraints[axis]; + axisValue.set(mixNumber(min, max, boxProgress[axis])); + }); + } + addListeners() { + if (!this.visualElement.current) + return; + elementDragControls.set(this.visualElement, this); + const element = this.visualElement.current; + /** + * Attach a pointerdown event listener on this DOM element to initiate drag tracking. + */ + const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => { + const { drag, dragListener = true } = this.getProps(); + drag && dragListener && this.start(event); + }); + const measureDragConstraints = () => { + const { dragConstraints } = this.getProps(); + if (isRefObject(dragConstraints) && dragConstraints.current) { + this.constraints = this.resolveRefConstraints(); + } + }; + const { projection } = this.visualElement; + const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints); + if (projection && !projection.layout) { + projection.root && projection.root.updateScroll(); + projection.updateLayout(); + } + frame.read(measureDragConstraints); + /** + * Attach a window resize listener to scale the draggable target within its defined + * constraints as the window resizes. + */ + const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints()); + /** + * If the element's layout changes, calculate the delta and apply that to + * the drag gesture's origin point. + */ + const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => { + if (this.isDragging && hasLayoutChanged) { + eachAxis((axis) => { + const motionValue = this.getAxisMotionValue(axis); + if (!motionValue) + return; + this.originPoint[axis] += delta[axis].translate; + motionValue.set(motionValue.get() + delta[axis].translate); + }); + this.visualElement.render(); + } + })); + return () => { + stopResizeListener(); + stopPointerListener(); + stopMeasureLayoutListener(); + stopLayoutUpdateListener && stopLayoutUpdateListener(); + }; + } + getProps() { + const props = this.visualElement.getProps(); + const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props; + return { + ...props, + drag, + dragDirectionLock, + dragPropagation, + dragConstraints, + dragElastic, + dragMomentum, + }; + } +} +function shouldDrag(direction, drag, currentDirection) { + return ((drag === true || drag === direction) && + (currentDirection === null || currentDirection === direction)); +} +/** + * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower + * than the provided threshold, return `null`. + * + * @param offset - The x/y offset from origin. + * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction. + */ +function getCurrentDirection(offset, lockThreshold = 10) { + let direction = null; + if (Math.abs(offset.y) > lockThreshold) { + direction = "y"; + } + else if (Math.abs(offset.x) > lockThreshold) { + direction = "x"; + } + return direction; +} + +export { VisualElementDragControls, elementDragControls }; diff --git a/node_modules/framer-motion/dist/es/gestures/drag/index.mjs b/node_modules/framer-motion/dist/es/gestures/drag/index.mjs new file mode 100644 index 00000000..5d968ae7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/drag/index.mjs @@ -0,0 +1,27 @@ +import { Feature } from '../../motion/features/Feature.mjs'; +import { noop } from 'motion-utils'; +import { VisualElementDragControls } from './VisualElementDragControls.mjs'; + +class DragGesture extends Feature { + constructor(node) { + super(node); + this.removeGroupControls = noop; + this.removeListeners = noop; + this.controls = new VisualElementDragControls(node); + } + mount() { + // If we've been provided a DragControls for manual control over the drag gesture, + // subscribe this component to it on mount. + const { dragControls } = this.node.getProps(); + if (dragControls) { + this.removeGroupControls = dragControls.subscribe(this.controls); + } + this.removeListeners = this.controls.addListeners() || noop; + } + unmount() { + this.removeGroupControls(); + this.removeListeners(); + } +} + +export { DragGesture }; diff --git a/node_modules/framer-motion/dist/es/gestures/drag/use-drag-controls.mjs b/node_modules/framer-motion/dist/es/gestures/drag/use-drag-controls.mjs new file mode 100644 index 00000000..5c2b45cb --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/drag/use-drag-controls.mjs @@ -0,0 +1,116 @@ +import { useConstant } from '../../utils/use-constant.mjs'; + +/** + * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ +class DragControls { + constructor() { + this.componentControls = new Set(); + } + /** + * Subscribe a component's internal `VisualElementDragControls` to the user-facing API. + * + * @internal + */ + subscribe(controls) { + this.componentControls.add(controls); + return () => this.componentControls.delete(controls); + } + /** + * Start a drag gesture on every `motion` component that has this set of drag controls + * passed into it via the `dragControls` prop. + * + * ```jsx + * dragControls.start(e, { + * snapToCursor: true + * }) + * ``` + * + * @param event - PointerEvent + * @param options - Options + * + * @public + */ + start(event, options) { + this.componentControls.forEach((controls) => { + controls.start(event.nativeEvent || event, options); + }); + } + /** + * Cancels a drag gesture. + * + * ```jsx + * dragControls.cancel() + * ``` + * + * @public + */ + cancel() { + this.componentControls.forEach((controls) => { + controls.cancel(); + }); + } + /** + * Stops a drag gesture. + * + * ```jsx + * dragControls.stop() + * ``` + * + * @public + */ + stop() { + this.componentControls.forEach((controls) => { + controls.stop(); + }); + } +} +const createDragControls = () => new DragControls(); +/** + * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop + * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we + * might want to initiate that dragging from a different component than the draggable one. + * + * By creating a `dragControls` using the `useDragControls` hook, we can pass this into + * the draggable component's `dragControls` prop. It exposes a `start` method + * that can start dragging from pointer events on other components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ +function useDragControls() { + return useConstant(createDragControls); +} + +export { DragControls, useDragControls }; diff --git a/node_modules/framer-motion/dist/es/gestures/drag/utils/constraints.mjs b/node_modules/framer-motion/dist/es/gestures/drag/utils/constraints.mjs new file mode 100644 index 00000000..5a39d8d6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/drag/utils/constraints.mjs @@ -0,0 +1,128 @@ +import { mixNumber } from 'motion-dom'; +import { progress, clamp } from 'motion-utils'; +import { calcLength } from '../../../projection/geometry/delta-calc.mjs'; + +/** + * Apply constraints to a point. These constraints are both physical along an + * axis, and an elastic factor that determines how much to constrain the point + * by if it does lie outside the defined parameters. + */ +function applyConstraints(point, { min, max }, elastic) { + if (min !== undefined && point < min) { + // If we have a min point defined, and this is outside of that, constrain + point = elastic + ? mixNumber(min, point, elastic.min) + : Math.max(point, min); + } + else if (max !== undefined && point > max) { + // If we have a max point defined, and this is outside of that, constrain + point = elastic + ? mixNumber(max, point, elastic.max) + : Math.min(point, max); + } + return point; +} +/** + * Calculate constraints in terms of the viewport when defined relatively to the + * measured axis. This is measured from the nearest edge, so a max constraint of 200 + * on an axis with a max value of 300 would return a constraint of 500 - axis length + */ +function calcRelativeAxisConstraints(axis, min, max) { + return { + min: min !== undefined ? axis.min + min : undefined, + max: max !== undefined + ? axis.max + max - (axis.max - axis.min) + : undefined, + }; +} +/** + * Calculate constraints in terms of the viewport when + * defined relatively to the measured bounding box. + */ +function calcRelativeConstraints(layoutBox, { top, left, bottom, right }) { + return { + x: calcRelativeAxisConstraints(layoutBox.x, left, right), + y: calcRelativeAxisConstraints(layoutBox.y, top, bottom), + }; +} +/** + * Calculate viewport constraints when defined as another viewport-relative axis + */ +function calcViewportAxisConstraints(layoutAxis, constraintsAxis) { + let min = constraintsAxis.min - layoutAxis.min; + let max = constraintsAxis.max - layoutAxis.max; + // If the constraints axis is actually smaller than the layout axis then we can + // flip the constraints + if (constraintsAxis.max - constraintsAxis.min < + layoutAxis.max - layoutAxis.min) { + [min, max] = [max, min]; + } + return { min, max }; +} +/** + * Calculate viewport constraints when defined as another viewport-relative box + */ +function calcViewportConstraints(layoutBox, constraintsBox) { + return { + x: calcViewportAxisConstraints(layoutBox.x, constraintsBox.x), + y: calcViewportAxisConstraints(layoutBox.y, constraintsBox.y), + }; +} +/** + * Calculate a transform origin relative to the source axis, between 0-1, that results + * in an asthetically pleasing scale/transform needed to project from source to target. + */ +function calcOrigin(source, target) { + let origin = 0.5; + const sourceLength = calcLength(source); + const targetLength = calcLength(target); + if (targetLength > sourceLength) { + origin = progress(target.min, target.max - sourceLength, source.min); + } + else if (sourceLength > targetLength) { + origin = progress(source.min, source.max - targetLength, target.min); + } + return clamp(0, 1, origin); +} +/** + * Rebase the calculated viewport constraints relative to the layout.min point. + */ +function rebaseAxisConstraints(layout, constraints) { + const relativeConstraints = {}; + if (constraints.min !== undefined) { + relativeConstraints.min = constraints.min - layout.min; + } + if (constraints.max !== undefined) { + relativeConstraints.max = constraints.max - layout.min; + } + return relativeConstraints; +} +const defaultElastic = 0.35; +/** + * Accepts a dragElastic prop and returns resolved elastic values for each axis. + */ +function resolveDragElastic(dragElastic = defaultElastic) { + if (dragElastic === false) { + dragElastic = 0; + } + else if (dragElastic === true) { + dragElastic = defaultElastic; + } + return { + x: resolveAxisElastic(dragElastic, "left", "right"), + y: resolveAxisElastic(dragElastic, "top", "bottom"), + }; +} +function resolveAxisElastic(dragElastic, minLabel, maxLabel) { + return { + min: resolvePointElastic(dragElastic, minLabel), + max: resolvePointElastic(dragElastic, maxLabel), + }; +} +function resolvePointElastic(dragElastic, label) { + return typeof dragElastic === "number" + ? dragElastic + : dragElastic[label] || 0; +} + +export { applyConstraints, calcOrigin, calcRelativeAxisConstraints, calcRelativeConstraints, calcViewportAxisConstraints, calcViewportConstraints, defaultElastic, rebaseAxisConstraints, resolveAxisElastic, resolveDragElastic, resolvePointElastic }; diff --git a/node_modules/framer-motion/dist/es/gestures/focus.mjs b/node_modules/framer-motion/dist/es/gestures/focus.mjs new file mode 100644 index 00000000..4ad76bf5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/focus.mjs @@ -0,0 +1,41 @@ +import { pipe } from 'motion-utils'; +import { addDomEvent } from '../events/add-dom-event.mjs'; +import { Feature } from '../motion/features/Feature.mjs'; + +class FocusGesture extends Feature { + constructor() { + super(...arguments); + this.isActive = false; + } + onFocus() { + let isFocusVisible = false; + /** + * If this element doesn't match focus-visible then don't + * apply whileHover. But, if matches throws that focus-visible + * is not a valid selector then in that browser outline styles will be applied + * to the element by default and we want to match that behaviour with whileFocus. + */ + try { + isFocusVisible = this.node.current.matches(":focus-visible"); + } + catch (e) { + isFocusVisible = true; + } + if (!isFocusVisible || !this.node.animationState) + return; + this.node.animationState.setActive("whileFocus", true); + this.isActive = true; + } + onBlur() { + if (!this.isActive || !this.node.animationState) + return; + this.node.animationState.setActive("whileFocus", false); + this.isActive = false; + } + mount() { + this.unmount = pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur())); + } + unmount() { } +} + +export { FocusGesture }; diff --git a/node_modules/framer-motion/dist/es/gestures/hover.mjs b/node_modules/framer-motion/dist/es/gestures/hover.mjs new file mode 100644 index 00000000..8a86a72b --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/hover.mjs @@ -0,0 +1,29 @@ +import { hover, frame } from 'motion-dom'; +import { extractEventInfo } from '../events/event-info.mjs'; +import { Feature } from '../motion/features/Feature.mjs'; + +function handleHoverEvent(node, event, lifecycle) { + const { props } = node; + if (node.animationState && props.whileHover) { + node.animationState.setActive("whileHover", lifecycle === "Start"); + } + const eventName = ("onHover" + lifecycle); + const callback = props[eventName]; + if (callback) { + frame.postRender(() => callback(event, extractEventInfo(event))); + } +} +class HoverGesture extends Feature { + mount() { + const { current } = this.node; + if (!current) + return; + this.unmount = hover(current, (_element, startEvent) => { + handleHoverEvent(this.node, startEvent, "Start"); + return (endEvent) => handleHoverEvent(this.node, endEvent, "End"); + }); + } + unmount() { } +} + +export { HoverGesture }; diff --git a/node_modules/framer-motion/dist/es/gestures/pan/PanSession.mjs b/node_modules/framer-motion/dist/es/gestures/pan/PanSession.mjs new file mode 100644 index 00000000..a690c270 --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/pan/PanSession.mjs @@ -0,0 +1,155 @@ +import { frame, isPrimaryPointer, cancelFrame, frameData } from 'motion-dom'; +import { pipe, secondsToMilliseconds, millisecondsToSeconds } from 'motion-utils'; +import { addPointerEvent } from '../../events/add-pointer-event.mjs'; +import { extractEventInfo } from '../../events/event-info.mjs'; +import { distance2D } from '../../utils/distance.mjs'; + +/** + * @internal + */ +class PanSession { + constructor(event, handlers, { transformPagePoint, contextWindow = window, dragSnapToOrigin = false, distanceThreshold = 3, } = {}) { + /** + * @internal + */ + this.startEvent = null; + /** + * @internal + */ + this.lastMoveEvent = null; + /** + * @internal + */ + this.lastMoveEventInfo = null; + /** + * @internal + */ + this.handlers = {}; + /** + * @internal + */ + this.contextWindow = window; + this.updatePoint = () => { + if (!(this.lastMoveEvent && this.lastMoveEventInfo)) + return; + const info = getPanInfo(this.lastMoveEventInfo, this.history); + const isPanStarted = this.startEvent !== null; + // Only start panning if the offset is larger than 3 pixels. If we make it + // any larger than this we'll want to reset the pointer history + // on the first update to avoid visual snapping to the cursor. + const isDistancePastThreshold = distance2D(info.offset, { x: 0, y: 0 }) >= this.distanceThreshold; + if (!isPanStarted && !isDistancePastThreshold) + return; + const { point } = info; + const { timestamp } = frameData; + this.history.push({ ...point, timestamp }); + const { onStart, onMove } = this.handlers; + if (!isPanStarted) { + onStart && onStart(this.lastMoveEvent, info); + this.startEvent = this.lastMoveEvent; + } + onMove && onMove(this.lastMoveEvent, info); + }; + this.handlePointerMove = (event, info) => { + this.lastMoveEvent = event; + this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint); + // Throttle mouse move event to once per frame + frame.update(this.updatePoint, true); + }; + this.handlePointerUp = (event, info) => { + this.end(); + const { onEnd, onSessionEnd, resumeAnimation } = this.handlers; + if (this.dragSnapToOrigin) + resumeAnimation && resumeAnimation(); + if (!(this.lastMoveEvent && this.lastMoveEventInfo)) + return; + const panInfo = getPanInfo(event.type === "pointercancel" + ? this.lastMoveEventInfo + : transformPoint(info, this.transformPagePoint), this.history); + if (this.startEvent && onEnd) { + onEnd(event, panInfo); + } + onSessionEnd && onSessionEnd(event, panInfo); + }; + // If we have more than one touch, don't start detecting this gesture + if (!isPrimaryPointer(event)) + return; + this.dragSnapToOrigin = dragSnapToOrigin; + this.handlers = handlers; + this.transformPagePoint = transformPagePoint; + this.distanceThreshold = distanceThreshold; + this.contextWindow = contextWindow || window; + const info = extractEventInfo(event); + const initialInfo = transformPoint(info, this.transformPagePoint); + const { point } = initialInfo; + const { timestamp } = frameData; + this.history = [{ ...point, timestamp }]; + const { onSessionStart } = handlers; + onSessionStart && + onSessionStart(event, getPanInfo(initialInfo, this.history)); + this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp)); + } + updateHandlers(handlers) { + this.handlers = handlers; + } + end() { + this.removeListeners && this.removeListeners(); + cancelFrame(this.updatePoint); + } +} +function transformPoint(info, transformPagePoint) { + return transformPagePoint ? { point: transformPagePoint(info.point) } : info; +} +function subtractPoint(a, b) { + return { x: a.x - b.x, y: a.y - b.y }; +} +function getPanInfo({ point }, history) { + return { + point, + delta: subtractPoint(point, lastDevicePoint(history)), + offset: subtractPoint(point, startDevicePoint(history)), + velocity: getVelocity(history, 0.1), + }; +} +function startDevicePoint(history) { + return history[0]; +} +function lastDevicePoint(history) { + return history[history.length - 1]; +} +function getVelocity(history, timeDelta) { + if (history.length < 2) { + return { x: 0, y: 0 }; + } + let i = history.length - 1; + let timestampedPoint = null; + const lastPoint = lastDevicePoint(history); + while (i >= 0) { + timestampedPoint = history[i]; + if (lastPoint.timestamp - timestampedPoint.timestamp > + secondsToMilliseconds(timeDelta)) { + break; + } + i--; + } + if (!timestampedPoint) { + return { x: 0, y: 0 }; + } + const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp); + if (time === 0) { + return { x: 0, y: 0 }; + } + const currentVelocity = { + x: (lastPoint.x - timestampedPoint.x) / time, + y: (lastPoint.y - timestampedPoint.y) / time, + }; + if (currentVelocity.x === Infinity) { + currentVelocity.x = 0; + } + if (currentVelocity.y === Infinity) { + currentVelocity.y = 0; + } + return currentVelocity; +} + +export { PanSession }; diff --git a/node_modules/framer-motion/dist/es/gestures/pan/index.mjs b/node_modules/framer-motion/dist/es/gestures/pan/index.mjs new file mode 100644 index 00000000..a6d64feb --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/pan/index.mjs @@ -0,0 +1,50 @@ +import { frame } from 'motion-dom'; +import { noop } from 'motion-utils'; +import { addPointerEvent } from '../../events/add-pointer-event.mjs'; +import { Feature } from '../../motion/features/Feature.mjs'; +import { getContextWindow } from '../../utils/get-context-window.mjs'; +import { PanSession } from './PanSession.mjs'; + +const asyncHandler = (handler) => (event, info) => { + if (handler) { + frame.postRender(() => handler(event, info)); + } +}; +class PanGesture extends Feature { + constructor() { + super(...arguments); + this.removePointerDownListener = noop; + } + onPointerDown(pointerDownEvent) { + this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), { + transformPagePoint: this.node.getTransformPagePoint(), + contextWindow: getContextWindow(this.node), + }); + } + createPanHandlers() { + const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps(); + return { + onSessionStart: asyncHandler(onPanSessionStart), + onStart: asyncHandler(onPanStart), + onMove: onPan, + onEnd: (event, info) => { + delete this.session; + if (onPanEnd) { + frame.postRender(() => onPanEnd(event, info)); + } + }, + }; + } + mount() { + this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event)); + } + update() { + this.session && this.session.updateHandlers(this.createPanHandlers()); + } + unmount() { + this.removePointerDownListener(); + this.session && this.session.end(); + } +} + +export { PanGesture }; diff --git a/node_modules/framer-motion/dist/es/gestures/press.mjs b/node_modules/framer-motion/dist/es/gestures/press.mjs new file mode 100644 index 00000000..2c749381 --- /dev/null +++ b/node_modules/framer-motion/dist/es/gestures/press.mjs @@ -0,0 +1,32 @@ +import { press, frame } from 'motion-dom'; +import { extractEventInfo } from '../events/event-info.mjs'; +import { Feature } from '../motion/features/Feature.mjs'; + +function handlePressEvent(node, event, lifecycle) { + const { props } = node; + if (node.current instanceof HTMLButtonElement && node.current.disabled) { + return; + } + if (node.animationState && props.whileTap) { + node.animationState.setActive("whileTap", lifecycle === "Start"); + } + const eventName = ("onTap" + (lifecycle === "End" ? "" : lifecycle)); + const callback = props[eventName]; + if (callback) { + frame.postRender(() => callback(event, extractEventInfo(event))); + } +} +class PressGesture extends Feature { + mount() { + const { current } = this.node; + if (!current) + return; + this.unmount = press(current, (_element, startEvent) => { + handlePressEvent(this.node, startEvent, "Start"); + return (endEvent, { success }) => handlePressEvent(this.node, endEvent, success ? "End" : "Cancel"); + }, { useGlobalTarget: this.node.props.globalTapTarget }); + } + unmount() { } +} + +export { PressGesture }; diff --git a/node_modules/framer-motion/dist/es/index.mjs b/node_modules/framer-motion/dist/es/index.mjs new file mode 100644 index 00000000..db8b8340 --- /dev/null +++ b/node_modules/framer-motion/dist/es/index.mjs @@ -0,0 +1,84 @@ +"use client"; +export { AnimatePresence } from './components/AnimatePresence/index.mjs'; +export { LayoutGroup } from './components/LayoutGroup/index.mjs'; +export { LazyMotion } from './components/LazyMotion/index.mjs'; +export { MotionConfig } from './components/MotionConfig/index.mjs'; +export { m } from './render/components/m/proxy.mjs'; +export { motion } from './render/components/motion/proxy.mjs'; +export { addPointerEvent } from './events/add-pointer-event.mjs'; +export { addPointerInfo } from './events/event-info.mjs'; +export { animations } from './motion/features/animations.mjs'; +export { makeUseVisualState } from './motion/utils/use-visual-state.mjs'; +export { calcLength } from './projection/geometry/delta-calc.mjs'; +export { createBox } from './projection/geometry/models.mjs'; +export { filterProps } from './render/dom/utils/filter-props.mjs'; +export { isBrowser } from './utils/is-browser.mjs'; +export { useForceUpdate } from './utils/use-force-update.mjs'; +export { useIsomorphicLayoutEffect } from './utils/use-isomorphic-effect.mjs'; +export { useUnmountEffect } from './utils/use-unmount-effect.mjs'; +export { domAnimation } from './render/dom/features-animation.mjs'; +export { domMax } from './render/dom/features-max.mjs'; +export { domMin } from './render/dom/features-min.mjs'; +export { useMotionValueEvent } from './utils/use-motion-value-event.mjs'; +export { useElementScroll } from './value/scroll/use-element-scroll.mjs'; +export { useViewportScroll } from './value/scroll/use-viewport-scroll.mjs'; +export { useMotionTemplate } from './value/use-motion-template.mjs'; +export { useMotionValue } from './value/use-motion-value.mjs'; +export { useScroll } from './value/use-scroll.mjs'; +export { useSpring } from './value/use-spring.mjs'; +export { useTime } from './value/use-time.mjs'; +export { useTransform } from './value/use-transform.mjs'; +export { useVelocity } from './value/use-velocity.mjs'; +export { useWillChange } from './value/use-will-change/index.mjs'; +export { WillChangeMotionValue } from './value/use-will-change/WillChangeMotionValue.mjs'; +export { resolveMotionValue } from './value/utils/resolve-motion-value.mjs'; +export { useReducedMotion } from './utils/reduced-motion/use-reduced-motion.mjs'; +export { useReducedMotionConfig } from './utils/reduced-motion/use-reduced-motion-config.mjs'; +export * from 'motion-utils'; +export { MotionGlobalConfig } from 'motion-utils'; +export { animationControls } from './animation/hooks/animation-controls.mjs'; +export { useAnimate } from './animation/hooks/use-animate.mjs'; +export { useAnimateMini } from './animation/hooks/use-animate-style.mjs'; +export { useAnimation, useAnimationControls } from './animation/hooks/use-animation.mjs'; +export { animateVisualElement } from './animation/interfaces/visual-element.mjs'; +export { useIsPresent, usePresence } from './components/AnimatePresence/use-presence.mjs'; +export { usePresenceData } from './components/AnimatePresence/use-presence-data.mjs'; +export { useDomEvent } from './events/use-dom-event.mjs'; +export { DragControls, useDragControls } from './gestures/drag/use-drag-controls.mjs'; +export { createRendererMotionComponent } from './motion/index.mjs'; +export { isMotionComponent } from './motion/utils/is-motion-component.mjs'; +export { unwrapMotionComponent } from './motion/utils/unwrap-motion-component.mjs'; +export { isValidMotionProp } from './motion/utils/valid-prop.mjs'; +export { addScaleCorrector } from './projection/styles/scale-correction.mjs'; +export { useInstantLayoutTransition } from './projection/use-instant-layout-transition.mjs'; +export { useResetProjection } from './projection/use-reset-projection.mjs'; +export { buildTransform } from './render/html/utils/build-transform.mjs'; +export { visualElementStore } from './render/store.mjs'; +export { VisualElement } from './render/VisualElement.mjs'; +export { useAnimationFrame } from './utils/use-animation-frame.mjs'; +export { useCycle } from './utils/use-cycle.mjs'; +export { useInView } from './utils/use-in-view.mjs'; +export { disableInstantTransitions, useInstantTransition } from './utils/use-instant-transition.mjs'; +export { usePageInView } from './utils/use-page-in-view.mjs'; +export { optimizedAppearDataAttribute } from './animation/optimized-appear/data-id.mjs'; +export { startOptimizedAppearAnimation } from './animation/optimized-appear/start.mjs'; +export { LayoutGroupContext } from './context/LayoutGroupContext.mjs'; +export { MotionConfigContext } from './context/MotionConfigContext.mjs'; +export { MotionContext } from './context/MotionContext/index.mjs'; +export { PresenceContext } from './context/PresenceContext.mjs'; +export { SwitchLayoutGroupContext } from './context/SwitchLayoutGroupContext.mjs'; +export { FlatTree } from './render/utils/flat-tree.mjs'; +export { useAnimatedState as useDeprecatedAnimatedState } from './animation/hooks/use-animated-state.mjs'; +export { AnimateSharedLayout } from './components/AnimateSharedLayout.mjs'; +export { DeprecatedLayoutGroupContext } from './context/DeprecatedLayoutGroupContext.mjs'; +export { useInvertedScale as useDeprecatedInvertedScale } from './value/use-inverted-scale.mjs'; +export { delay } from './utils/delay.mjs'; +import * as namespace from './components/Reorder/namespace.mjs'; +export { namespace as Reorder }; +export { animate, createScopedAnimate } from './animation/animate/index.mjs'; +export { animateMini } from './animation/animators/waapi/animate-style.mjs'; +export { scroll } from './render/dom/scroll/index.mjs'; +export { scrollInfo } from './render/dom/scroll/track.mjs'; +export { inView } from './render/dom/viewport/index.mjs'; +export { distance, distance2D } from './utils/distance.mjs'; +export * from 'motion-dom'; diff --git a/node_modules/framer-motion/dist/es/m.mjs b/node_modules/framer-motion/dist/es/m.mjs new file mode 100644 index 00000000..5db207de --- /dev/null +++ b/node_modules/framer-motion/dist/es/m.mjs @@ -0,0 +1,3 @@ +"use client"; +export { createMinimalMotionComponent as create } from './render/components/m/create.mjs'; +export { MotionA as a, MotionAbbr as abbr, MotionAddress as address, MotionAnimate as animate, MotionArea as area, MotionArticle as article, MotionAside as aside, MotionAudio as audio, MotionB as b, MotionBase as base, MotionBdi as bdi, MotionBdo as bdo, MotionBig as big, MotionBlockquote as blockquote, MotionBody as body, MotionButton as button, MotionCanvas as canvas, MotionCaption as caption, MotionCircle as circle, MotionCite as cite, MotionClipPath as clipPath, MotionCode as code, MotionCol as col, MotionColgroup as colgroup, MotionData as data, MotionDatalist as datalist, MotionDd as dd, MotionDefs as defs, MotionDel as del, MotionDesc as desc, MotionDetails as details, MotionDfn as dfn, MotionDialog as dialog, MotionDiv as div, MotionDl as dl, MotionDt as dt, MotionEllipse as ellipse, MotionEm as em, MotionEmbed as embed, MotionFeBlend as feBlend, MotionFeColorMatrix as feColorMatrix, MotionFeComponentTransfer as feComponentTransfer, MotionFeComposite as feComposite, MotionFeConvolveMatrix as feConvolveMatrix, MotionFeDiffuseLighting as feDiffuseLighting, MotionFeDisplacementMap as feDisplacementMap, MotionFeDistantLight as feDistantLight, MotionFeDropShadow as feDropShadow, MotionFeFlood as feFlood, MotionFeFuncA as feFuncA, MotionFeFuncB as feFuncB, MotionFeFuncG as feFuncG, MotionFeFuncR as feFuncR, MotionFeGaussianBlur as feGaussianBlur, MotionFeImage as feImage, MotionFeMerge as feMerge, MotionFeMergeNode as feMergeNode, MotionFeMorphology as feMorphology, MotionFeOffset as feOffset, MotionFePointLight as fePointLight, MotionFeSpecularLighting as feSpecularLighting, MotionFeSpotLight as feSpotLight, MotionFeTile as feTile, MotionFeTurbulence as feTurbulence, MotionFieldset as fieldset, MotionFigcaption as figcaption, MotionFigure as figure, MotionFilter as filter, MotionFooter as footer, MotionForeignObject as foreignObject, MotionForm as form, MotionG as g, MotionH1 as h1, MotionH2 as h2, MotionH3 as h3, MotionH4 as h4, MotionH5 as h5, MotionH6 as h6, MotionHead as head, MotionHeader as header, MotionHgroup as hgroup, MotionHr as hr, MotionHtml as html, MotionI as i, MotionIframe as iframe, MotionImage as image, MotionImg as img, MotionInput as input, MotionIns as ins, MotionKbd as kbd, MotionKeygen as keygen, MotionLabel as label, MotionLegend as legend, MotionLi as li, MotionLine as line, MotionLinearGradient as linearGradient, MotionLink as link, MotionMain as main, MotionMap as map, MotionMark as mark, MotionMarker as marker, MotionMask as mask, MotionMenu as menu, MotionMenuitem as menuitem, MotionMetadata as metadata, MotionMeter as meter, MotionNav as nav, MotionObject as object, MotionOl as ol, MotionOptgroup as optgroup, MotionOption as option, MotionOutput as output, MotionP as p, MotionParam as param, MotionPath as path, MotionPattern as pattern, MotionPicture as picture, MotionPolygon as polygon, MotionPolyline as polyline, MotionPre as pre, MotionProgress as progress, MotionQ as q, MotionRadialGradient as radialGradient, MotionRect as rect, MotionRp as rp, MotionRt as rt, MotionRuby as ruby, MotionS as s, MotionSamp as samp, MotionScript as script, MotionSection as section, MotionSelect as select, MotionSmall as small, MotionSource as source, MotionSpan as span, MotionStop as stop, MotionStrong as strong, MotionStyle as style, MotionSub as sub, MotionSummary as summary, MotionSup as sup, MotionSvg as svg, MotionSymbol as symbol, MotionTable as table, MotionTbody as tbody, MotionTd as td, MotionText as text, MotionTextPath as textPath, MotionTextarea as textarea, MotionTfoot as tfoot, MotionTh as th, MotionThead as thead, MotionTime as time, MotionTitle as title, MotionTr as tr, MotionTrack as track, MotionTspan as tspan, MotionU as u, MotionUl as ul, MotionUse as use, MotionVideo as video, MotionView as view, MotionWbr as wbr, MotionWebview as webview } from './render/components/m/elements.mjs'; diff --git a/node_modules/framer-motion/dist/es/mini.mjs b/node_modules/framer-motion/dist/es/mini.mjs new file mode 100644 index 00000000..e2552976 --- /dev/null +++ b/node_modules/framer-motion/dist/es/mini.mjs @@ -0,0 +1 @@ +export { useAnimateMini as useAnimate } from './animation/hooks/use-animate-style.mjs'; diff --git a/node_modules/framer-motion/dist/es/motion/features/Feature.mjs b/node_modules/framer-motion/dist/es/motion/features/Feature.mjs new file mode 100644 index 00000000..1c70050e --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/Feature.mjs @@ -0,0 +1,9 @@ +class Feature { + constructor(node) { + this.isMounted = false; + this.node = node; + } + update() { } +} + +export { Feature }; diff --git a/node_modules/framer-motion/dist/es/motion/features/animation/exit.mjs b/node_modules/framer-motion/dist/es/motion/features/animation/exit.mjs new file mode 100644 index 00000000..4c1a92be --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/animation/exit.mjs @@ -0,0 +1,36 @@ +import { Feature } from '../Feature.mjs'; + +let id = 0; +class ExitAnimationFeature extends Feature { + constructor() { + super(...arguments); + this.id = id++; + } + update() { + if (!this.node.presenceContext) + return; + const { isPresent, onExitComplete } = this.node.presenceContext; + const { isPresent: prevIsPresent } = this.node.prevPresenceContext || {}; + if (!this.node.animationState || isPresent === prevIsPresent) { + return; + } + const exitAnimation = this.node.animationState.setActive("exit", !isPresent); + if (onExitComplete && !isPresent) { + exitAnimation.then(() => { + onExitComplete(this.id); + }); + } + } + mount() { + const { register, onExitComplete } = this.node.presenceContext || {}; + if (onExitComplete) { + onExitComplete(this.id); + } + if (register) { + this.unmount = register(this.id); + } + } + unmount() { } +} + +export { ExitAnimationFeature }; diff --git a/node_modules/framer-motion/dist/es/motion/features/animation/index.mjs b/node_modules/framer-motion/dist/es/motion/features/animation/index.mjs new file mode 100644 index 00000000..5a1eae94 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/animation/index.mjs @@ -0,0 +1,40 @@ +import { isAnimationControls } from '../../../animation/utils/is-animation-controls.mjs'; +import { createAnimationState } from '../../../render/utils/animation-state.mjs'; +import { Feature } from '../Feature.mjs'; + +class AnimationFeature extends Feature { + /** + * We dynamically generate the AnimationState manager as it contains a reference + * to the underlying animation library. We only want to load that if we load this, + * so people can optionally code split it out using the `m` component. + */ + constructor(node) { + super(node); + node.animationState || (node.animationState = createAnimationState(node)); + } + updateAnimationControlsSubscription() { + const { animate } = this.node.getProps(); + if (isAnimationControls(animate)) { + this.unmountControls = animate.subscribe(this.node); + } + } + /** + * Subscribe any provided AnimationControls to the component's VisualElement + */ + mount() { + this.updateAnimationControlsSubscription(); + } + update() { + const { animate } = this.node.getProps(); + const { animate: prevAnimate } = this.node.prevProps || {}; + if (animate !== prevAnimate) { + this.updateAnimationControlsSubscription(); + } + } + unmount() { + this.node.animationState.reset(); + this.unmountControls?.(); + } +} + +export { AnimationFeature }; diff --git a/node_modules/framer-motion/dist/es/motion/features/animations.mjs b/node_modules/framer-motion/dist/es/motion/features/animations.mjs new file mode 100644 index 00000000..bd67eb40 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/animations.mjs @@ -0,0 +1,13 @@ +import { AnimationFeature } from './animation/index.mjs'; +import { ExitAnimationFeature } from './animation/exit.mjs'; + +const animations = { + animation: { + Feature: AnimationFeature, + }, + exit: { + Feature: ExitAnimationFeature, + }, +}; + +export { animations }; diff --git a/node_modules/framer-motion/dist/es/motion/features/definitions.mjs b/node_modules/framer-motion/dist/es/motion/features/definitions.mjs new file mode 100644 index 00000000..5a774ad5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/definitions.mjs @@ -0,0 +1,28 @@ +const featureProps = { + animation: [ + "animate", + "variants", + "whileHover", + "whileTap", + "exit", + "whileInView", + "whileFocus", + "whileDrag", + ], + exit: ["exit"], + drag: ["drag", "dragControls"], + focus: ["whileFocus"], + hover: ["whileHover", "onHoverStart", "onHoverEnd"], + tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"], + pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"], + inView: ["whileInView", "onViewportEnter", "onViewportLeave"], + layout: ["layout", "layoutId"], +}; +const featureDefinitions = {}; +for (const key in featureProps) { + featureDefinitions[key] = { + isEnabled: (props) => featureProps[key].some((name) => !!props[name]), + }; +} + +export { featureDefinitions }; diff --git a/node_modules/framer-motion/dist/es/motion/features/drag.mjs b/node_modules/framer-motion/dist/es/motion/features/drag.mjs new file mode 100644 index 00000000..ecac2141 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/drag.mjs @@ -0,0 +1,17 @@ +import { DragGesture } from '../../gestures/drag/index.mjs'; +import { PanGesture } from '../../gestures/pan/index.mjs'; +import { MeasureLayout } from './layout/MeasureLayout.mjs'; +import { HTMLProjectionNode } from '../../projection/node/HTMLProjectionNode.mjs'; + +const drag = { + pan: { + Feature: PanGesture, + }, + drag: { + Feature: DragGesture, + ProjectionNode: HTMLProjectionNode, + MeasureLayout, + }, +}; + +export { drag }; diff --git a/node_modules/framer-motion/dist/es/motion/features/gestures.mjs b/node_modules/framer-motion/dist/es/motion/features/gestures.mjs new file mode 100644 index 00000000..a9a9cf67 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/gestures.mjs @@ -0,0 +1,21 @@ +import { HoverGesture } from '../../gestures/hover.mjs'; +import { FocusGesture } from '../../gestures/focus.mjs'; +import { PressGesture } from '../../gestures/press.mjs'; +import { InViewFeature } from './viewport/index.mjs'; + +const gestureAnimations = { + inView: { + Feature: InViewFeature, + }, + tap: { + Feature: PressGesture, + }, + focus: { + Feature: FocusGesture, + }, + hover: { + Feature: HoverGesture, + }, +}; + +export { gestureAnimations }; diff --git a/node_modules/framer-motion/dist/es/motion/features/layout.mjs b/node_modules/framer-motion/dist/es/motion/features/layout.mjs new file mode 100644 index 00000000..e473842e --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/layout.mjs @@ -0,0 +1,11 @@ +import { HTMLProjectionNode } from '../../projection/node/HTMLProjectionNode.mjs'; +import { MeasureLayout } from './layout/MeasureLayout.mjs'; + +const layout = { + layout: { + ProjectionNode: HTMLProjectionNode, + MeasureLayout, + }, +}; + +export { layout }; diff --git a/node_modules/framer-motion/dist/es/motion/features/layout/MeasureLayout.mjs b/node_modules/framer-motion/dist/es/motion/features/layout/MeasureLayout.mjs new file mode 100644 index 00000000..2cebc59d --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/layout/MeasureLayout.mjs @@ -0,0 +1,142 @@ +"use client"; +import { jsx } from 'react/jsx-runtime'; +import { frame, microtask } from 'motion-dom'; +import { useContext, Component } from 'react'; +import { usePresence } from '../../../components/AnimatePresence/use-presence.mjs'; +import { LayoutGroupContext } from '../../../context/LayoutGroupContext.mjs'; +import { SwitchLayoutGroupContext } from '../../../context/SwitchLayoutGroupContext.mjs'; +import { globalProjectionState } from '../../../projection/node/state.mjs'; +import { correctBorderRadius } from '../../../projection/styles/scale-border-radius.mjs'; +import { correctBoxShadow } from '../../../projection/styles/scale-box-shadow.mjs'; +import { addScaleCorrector } from '../../../projection/styles/scale-correction.mjs'; + +/** + * Track whether we've taken any snapshots yet. If not, + * we can safely skip notification of didUpdate. + */ +let hasTakenAnySnapshot = false; +class MeasureLayoutWithContext extends Component { + /** + * This only mounts projection nodes for components that + * need measuring, we might want to do it for all components + * in order to incorporate transforms + */ + componentDidMount() { + const { visualElement, layoutGroup, switchLayoutGroup, layoutId } = this.props; + const { projection } = visualElement; + addScaleCorrector(defaultScaleCorrectors); + if (projection) { + if (layoutGroup.group) + layoutGroup.group.add(projection); + if (switchLayoutGroup && switchLayoutGroup.register && layoutId) { + switchLayoutGroup.register(projection); + } + if (hasTakenAnySnapshot) { + projection.root.didUpdate(); + } + projection.addEventListener("animationComplete", () => { + this.safeToRemove(); + }); + projection.setOptions({ + ...projection.options, + onExitComplete: () => this.safeToRemove(), + }); + } + globalProjectionState.hasEverUpdated = true; + } + getSnapshotBeforeUpdate(prevProps) { + const { layoutDependency, visualElement, drag, isPresent } = this.props; + const { projection } = visualElement; + if (!projection) + return null; + /** + * TODO: We use this data in relegate to determine whether to + * promote a previous element. There's no guarantee its presence data + * will have updated by this point - if a bug like this arises it will + * have to be that we markForRelegation and then find a new lead some other way, + * perhaps in didUpdate + */ + projection.isPresent = isPresent; + hasTakenAnySnapshot = true; + if (drag || + prevProps.layoutDependency !== layoutDependency || + layoutDependency === undefined || + prevProps.isPresent !== isPresent) { + projection.willUpdate(); + } + else { + this.safeToRemove(); + } + if (prevProps.isPresent !== isPresent) { + if (isPresent) { + projection.promote(); + } + else if (!projection.relegate()) { + /** + * If there's another stack member taking over from this one, + * it's in charge of the exit animation and therefore should + * be in charge of the safe to remove. Otherwise we call it here. + */ + frame.postRender(() => { + const stack = projection.getStack(); + if (!stack || !stack.members.length) { + this.safeToRemove(); + } + }); + } + } + return null; + } + componentDidUpdate() { + const { projection } = this.props.visualElement; + if (projection) { + projection.root.didUpdate(); + microtask.postRender(() => { + if (!projection.currentAnimation && projection.isLead()) { + this.safeToRemove(); + } + }); + } + } + componentWillUnmount() { + const { visualElement, layoutGroup, switchLayoutGroup: promoteContext, } = this.props; + const { projection } = visualElement; + if (projection) { + projection.scheduleCheckAfterUnmount(); + if (layoutGroup && layoutGroup.group) + layoutGroup.group.remove(projection); + if (promoteContext && promoteContext.deregister) + promoteContext.deregister(projection); + } + } + safeToRemove() { + const { safeToRemove } = this.props; + safeToRemove && safeToRemove(); + } + render() { + return null; + } +} +function MeasureLayout(props) { + const [isPresent, safeToRemove] = usePresence(); + const layoutGroup = useContext(LayoutGroupContext); + return (jsx(MeasureLayoutWithContext, { ...props, layoutGroup: layoutGroup, switchLayoutGroup: useContext(SwitchLayoutGroupContext), isPresent: isPresent, safeToRemove: safeToRemove })); +} +const defaultScaleCorrectors = { + borderRadius: { + ...correctBorderRadius, + applyTo: [ + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomLeftRadius", + "borderBottomRightRadius", + ], + }, + borderTopLeftRadius: correctBorderRadius, + borderTopRightRadius: correctBorderRadius, + borderBottomLeftRadius: correctBorderRadius, + borderBottomRightRadius: correctBorderRadius, + boxShadow: correctBoxShadow, +}; + +export { MeasureLayout }; diff --git a/node_modules/framer-motion/dist/es/motion/features/load-features.mjs b/node_modules/framer-motion/dist/es/motion/features/load-features.mjs new file mode 100644 index 00000000..9211bba8 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/load-features.mjs @@ -0,0 +1,12 @@ +import { featureDefinitions } from './definitions.mjs'; + +function loadFeatures(features) { + for (const key in features) { + featureDefinitions[key] = { + ...featureDefinitions[key], + ...features[key], + }; + } +} + +export { loadFeatures }; diff --git a/node_modules/framer-motion/dist/es/motion/features/viewport/index.mjs b/node_modules/framer-motion/dist/es/motion/features/viewport/index.mjs new file mode 100644 index 00000000..29198b49 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/viewport/index.mjs @@ -0,0 +1,72 @@ +import { Feature } from '../Feature.mjs'; +import { observeIntersection } from './observers.mjs'; + +const thresholdNames = { + some: 0, + all: 1, +}; +class InViewFeature extends Feature { + constructor() { + super(...arguments); + this.hasEnteredView = false; + this.isInView = false; + } + startObserver() { + this.unmount(); + const { viewport = {} } = this.node.getProps(); + const { root, margin: rootMargin, amount = "some", once } = viewport; + const options = { + root: root ? root.current : undefined, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholdNames[amount], + }; + const onIntersectionUpdate = (entry) => { + const { isIntersecting } = entry; + /** + * If there's been no change in the viewport state, early return. + */ + if (this.isInView === isIntersecting) + return; + this.isInView = isIntersecting; + /** + * Handle hasEnteredView. If this is only meant to run once, and + * element isn't visible, early return. Otherwise set hasEnteredView to true. + */ + if (once && !isIntersecting && this.hasEnteredView) { + return; + } + else if (isIntersecting) { + this.hasEnteredView = true; + } + if (this.node.animationState) { + this.node.animationState.setActive("whileInView", isIntersecting); + } + /** + * Use the latest committed props rather than the ones in scope + * when this observer is created + */ + const { onViewportEnter, onViewportLeave } = this.node.getProps(); + const callback = isIntersecting ? onViewportEnter : onViewportLeave; + callback && callback(entry); + }; + return observeIntersection(this.node.current, options, onIntersectionUpdate); + } + mount() { + this.startObserver(); + } + update() { + if (typeof IntersectionObserver === "undefined") + return; + const { props, prevProps } = this.node; + const hasOptionsChanged = ["amount", "margin", "root"].some(hasViewportOptionChanged(props, prevProps)); + if (hasOptionsChanged) { + this.startObserver(); + } + } + unmount() { } +} +function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) { + return (name) => viewport[name] !== prevViewport[name]; +} + +export { InViewFeature }; diff --git a/node_modules/framer-motion/dist/es/motion/features/viewport/observers.mjs b/node_modules/framer-motion/dist/es/motion/features/viewport/observers.mjs new file mode 100644 index 00000000..36106045 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/features/viewport/observers.mjs @@ -0,0 +1,49 @@ +/** + * Map an IntersectionHandler callback to an element. We only ever make one handler for one + * element, so even though these handlers might all be triggered by different + * observers, we can keep them in the same map. + */ +const observerCallbacks = new WeakMap(); +/** + * Multiple observers can be created for multiple element/document roots. Each with + * different settings. So here we store dictionaries of observers to each root, + * using serialised settings (threshold/margin) as lookup keys. + */ +const observers = new WeakMap(); +const fireObserverCallback = (entry) => { + const callback = observerCallbacks.get(entry.target); + callback && callback(entry); +}; +const fireAllObserverCallbacks = (entries) => { + entries.forEach(fireObserverCallback); +}; +function initIntersectionObserver({ root, ...options }) { + const lookupRoot = root || document; + /** + * If we don't have an observer lookup map for this root, create one. + */ + if (!observers.has(lookupRoot)) { + observers.set(lookupRoot, {}); + } + const rootObservers = observers.get(lookupRoot); + const key = JSON.stringify(options); + /** + * If we don't have an observer for this combination of root and settings, + * create one. + */ + if (!rootObservers[key]) { + rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, { root, ...options }); + } + return rootObservers[key]; +} +function observeIntersection(element, options, callback) { + const rootInteresectionObserver = initIntersectionObserver(options); + observerCallbacks.set(element, callback); + rootInteresectionObserver.observe(element); + return () => { + observerCallbacks.delete(element); + rootInteresectionObserver.unobserve(element); + }; +} + +export { observeIntersection }; diff --git a/node_modules/framer-motion/dist/es/motion/index.mjs b/node_modules/framer-motion/dist/es/motion/index.mjs new file mode 100644 index 00000000..32ff44d8 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/index.mjs @@ -0,0 +1,101 @@ +"use client"; +import { jsxs, jsx } from 'react/jsx-runtime'; +import { warning, invariant } from 'motion-utils'; +import { forwardRef, useContext } from 'react'; +import { LayoutGroupContext } from '../context/LayoutGroupContext.mjs'; +import { LazyContext } from '../context/LazyContext.mjs'; +import { MotionConfigContext } from '../context/MotionConfigContext.mjs'; +import { MotionContext } from '../context/MotionContext/index.mjs'; +import { useCreateMotionContext } from '../context/MotionContext/create.mjs'; +import { isBrowser } from '../utils/is-browser.mjs'; +import { featureDefinitions } from './features/definitions.mjs'; +import { loadFeatures } from './features/load-features.mjs'; +import { motionComponentSymbol } from './utils/symbol.mjs'; +import { useMotionRef } from './utils/use-motion-ref.mjs'; +import { useVisualElement } from './utils/use-visual-element.mjs'; + +/** + * Create a `motion` component. + * + * This function accepts a Component argument, which can be either a string (ie "div" + * for `motion.div`), or an actual React component. + * + * Alongside this is a config option which provides a way of rendering the provided + * component "offline", or outside the React render cycle. + */ +function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) { + preloadedFeatures && loadFeatures(preloadedFeatures); + function MotionComponent(props, externalRef) { + /** + * If we need to measure the element we load this functionality in a + * separate class component in order to gain access to getSnapshotBeforeUpdate. + */ + let MeasureLayout; + const configAndProps = { + ...useContext(MotionConfigContext), + ...props, + layoutId: useLayoutId(props), + }; + const { isStatic } = configAndProps; + const context = useCreateMotionContext(props); + const visualState = useVisualState(props, isStatic); + if (!isStatic && isBrowser) { + useStrictMode(configAndProps, preloadedFeatures); + const layoutProjection = getProjectionFunctionality(configAndProps); + MeasureLayout = layoutProjection.MeasureLayout; + /** + * Create a VisualElement for this component. A VisualElement provides a common + * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as + * providing a way of rendering to these APIs outside of the React render loop + * for more performant animations and interactions + */ + context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode); + } + /** + * The mount order and hierarchy is specific to ensure our element ref + * is hydrated by the time features fire their effects. + */ + return (jsxs(MotionContext.Provider, { value: context, children: [MeasureLayout && context.visualElement ? (jsx(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null, useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)] })); + } + MotionComponent.displayName = `motion.${typeof Component === "string" + ? Component + : `create(${Component.displayName ?? Component.name ?? ""})`}`; + const ForwardRefMotionComponent = forwardRef(MotionComponent); + ForwardRefMotionComponent[motionComponentSymbol] = Component; + return ForwardRefMotionComponent; +} +function useLayoutId({ layoutId }) { + const layoutGroupId = useContext(LayoutGroupContext).id; + return layoutGroupId && layoutId !== undefined + ? layoutGroupId + "-" + layoutId + : layoutId; +} +function useStrictMode(configAndProps, preloadedFeatures) { + const isStrict = useContext(LazyContext).strict; + /** + * If we're in development mode, check to make sure we're not rendering a motion component + * as a child of LazyMotion, as this will break the file-size benefits of using it. + */ + if (process.env.NODE_ENV !== "production" && + preloadedFeatures && + isStrict) { + const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead."; + configAndProps.ignoreStrict + ? warning(false, strictMessage) + : invariant(false, strictMessage); + } +} +function getProjectionFunctionality(props) { + const { drag, layout } = featureDefinitions; + if (!drag && !layout) + return {}; + const combined = { ...drag, ...layout }; + return { + MeasureLayout: drag?.isEnabled(props) || layout?.isEnabled(props) + ? combined.MeasureLayout + : undefined, + ProjectionNode: combined.ProjectionNode, + }; +} + +export { createRendererMotionComponent }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/is-forced-motion-value.mjs b/node_modules/framer-motion/dist/es/motion/utils/is-forced-motion-value.mjs new file mode 100644 index 00000000..59d74f3c --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/is-forced-motion-value.mjs @@ -0,0 +1,11 @@ +import { transformProps } from 'motion-dom'; +import { scaleCorrectors } from '../../projection/styles/scale-correction.mjs'; + +function isForcedMotionValue(key, { layout, layoutId }) { + return (transformProps.has(key) || + key.startsWith("origin") || + ((layout || layoutId !== undefined) && + (!!scaleCorrectors[key] || key === "opacity"))); +} + +export { isForcedMotionValue }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/is-motion-component.mjs b/node_modules/framer-motion/dist/es/motion/utils/is-motion-component.mjs new file mode 100644 index 00000000..6ddb65cd --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/is-motion-component.mjs @@ -0,0 +1,12 @@ +import { motionComponentSymbol } from './symbol.mjs'; + +/** + * Checks if a component is a `motion` component. + */ +function isMotionComponent(component) { + return (component !== null && + typeof component === "object" && + motionComponentSymbol in component); +} + +export { isMotionComponent }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/symbol.mjs b/node_modules/framer-motion/dist/es/motion/utils/symbol.mjs new file mode 100644 index 00000000..376932c9 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/symbol.mjs @@ -0,0 +1,3 @@ +const motionComponentSymbol = Symbol.for("motionComponentSymbol"); + +export { motionComponentSymbol }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/unwrap-motion-component.mjs b/node_modules/framer-motion/dist/es/motion/utils/unwrap-motion-component.mjs new file mode 100644 index 00000000..204db9b4 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/unwrap-motion-component.mjs @@ -0,0 +1,17 @@ +import { isMotionComponent } from './is-motion-component.mjs'; +import { motionComponentSymbol } from './symbol.mjs'; + +/** + * Unwraps a `motion` component and returns either a string for `motion.div` or + * the React component for `motion(Component)`. + * + * If the component is not a `motion` component it returns undefined. + */ +function unwrapMotionComponent(component) { + if (isMotionComponent(component)) { + return component[motionComponentSymbol]; + } + return undefined; +} + +export { unwrapMotionComponent }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/use-motion-ref.mjs b/node_modules/framer-motion/dist/es/motion/utils/use-motion-ref.mjs new file mode 100644 index 00000000..6c2807fe --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/use-motion-ref.mjs @@ -0,0 +1,38 @@ +import { useCallback } from 'react'; +import { isRefObject } from '../../utils/is-ref-object.mjs'; + +/** + * Creates a ref function that, when called, hydrates the provided + * external ref and VisualElement. + */ +function useMotionRef(visualState, visualElement, externalRef) { + return useCallback((instance) => { + if (instance) { + visualState.onMount && visualState.onMount(instance); + } + if (visualElement) { + if (instance) { + visualElement.mount(instance); + } + else { + visualElement.unmount(); + } + } + if (externalRef) { + if (typeof externalRef === "function") { + externalRef(instance); + } + else if (isRefObject(externalRef)) { + externalRef.current = instance; + } + } + }, + /** + * Only pass a new ref callback to React if we've received a visual element + * factory. Otherwise we'll be mounting/remounting every time externalRef + * or other dependencies change. + */ + [visualElement]); +} + +export { useMotionRef }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/use-visual-element.mjs b/node_modules/framer-motion/dist/es/motion/utils/use-visual-element.mjs new file mode 100644 index 00000000..57e38398 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/use-visual-element.mjs @@ -0,0 +1,133 @@ +import { microtask } from 'motion-dom'; +import { useContext, useRef, useInsertionEffect, useEffect } from 'react'; +import { optimizedAppearDataAttribute } from '../../animation/optimized-appear/data-id.mjs'; +import { LazyContext } from '../../context/LazyContext.mjs'; +import { MotionConfigContext } from '../../context/MotionConfigContext.mjs'; +import { MotionContext } from '../../context/MotionContext/index.mjs'; +import { PresenceContext } from '../../context/PresenceContext.mjs'; +import { SwitchLayoutGroupContext } from '../../context/SwitchLayoutGroupContext.mjs'; +import { isRefObject } from '../../utils/is-ref-object.mjs'; +import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs'; + +function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor) { + const { visualElement: parent } = useContext(MotionContext); + const lazyContext = useContext(LazyContext); + const presenceContext = useContext(PresenceContext); + const reducedMotionConfig = useContext(MotionConfigContext).reducedMotion; + const visualElementRef = useRef(null); + /** + * If we haven't preloaded a renderer, check to see if we have one lazy-loaded + */ + createVisualElement = createVisualElement || lazyContext.renderer; + if (!visualElementRef.current && createVisualElement) { + visualElementRef.current = createVisualElement(Component, { + visualState, + parent, + props, + presenceContext, + blockInitialAnimation: presenceContext + ? presenceContext.initial === false + : false, + reducedMotionConfig, + }); + } + const visualElement = visualElementRef.current; + /** + * Load Motion gesture and animation features. These are rendered as renderless + * components so each feature can optionally make use of React lifecycle methods. + */ + const initialLayoutGroupConfig = useContext(SwitchLayoutGroupContext); + if (visualElement && + !visualElement.projection && + ProjectionNodeConstructor && + (visualElement.type === "html" || visualElement.type === "svg")) { + createProjectionNode(visualElementRef.current, props, ProjectionNodeConstructor, initialLayoutGroupConfig); + } + const isMounted = useRef(false); + useInsertionEffect(() => { + /** + * Check the component has already mounted before calling + * `update` unnecessarily. This ensures we skip the initial update. + */ + if (visualElement && isMounted.current) { + visualElement.update(props, presenceContext); + } + }); + /** + * Cache this value as we want to know whether HandoffAppearAnimations + * was present on initial render - it will be deleted after this. + */ + const optimisedAppearId = props[optimizedAppearDataAttribute]; + const wantsHandoff = useRef(Boolean(optimisedAppearId) && + !window.MotionHandoffIsComplete?.(optimisedAppearId) && + window.MotionHasOptimisedAnimation?.(optimisedAppearId)); + useIsomorphicLayoutEffect(() => { + if (!visualElement) + return; + isMounted.current = true; + window.MotionIsMounted = true; + visualElement.updateFeatures(); + microtask.render(visualElement.render); + /** + * Ideally this function would always run in a useEffect. + * + * However, if we have optimised appear animations to handoff from, + * it needs to happen synchronously to ensure there's no flash of + * incorrect styles in the event of a hydration error. + * + * So if we detect a situtation where optimised appear animations + * are running, we use useLayoutEffect to trigger animations. + */ + if (wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + }); + useEffect(() => { + if (!visualElement) + return; + if (!wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + if (wantsHandoff.current) { + // This ensures all future calls to animateChanges() in this component will run in useEffect + queueMicrotask(() => { + window.MotionHandoffMarkAsComplete?.(optimisedAppearId); + }); + wantsHandoff.current = false; + } + }); + return visualElement; +} +function createProjectionNode(visualElement, props, ProjectionNodeConstructor, initialPromotionConfig) { + const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, layoutCrossfade, } = props; + visualElement.projection = new ProjectionNodeConstructor(visualElement.latestValues, props["data-framer-portal-id"] + ? undefined + : getClosestProjectingNode(visualElement.parent)); + visualElement.projection.setOptions({ + layoutId, + layout, + alwaysMeasureLayout: Boolean(drag) || (dragConstraints && isRefObject(dragConstraints)), + visualElement, + /** + * TODO: Update options in an effect. This could be tricky as it'll be too late + * to update by the time layout animations run. + * We also need to fix this safeToRemove by linking it up to the one returned by usePresence, + * ensuring it gets called if there's no potential layout animations. + * + */ + animationType: typeof layout === "string" ? layout : "both", + initialPromotionConfig, + crossfade: layoutCrossfade, + layoutScroll, + layoutRoot, + }); +} +function getClosestProjectingNode(visualElement) { + if (!visualElement) + return undefined; + return visualElement.options.allowProjection !== false + ? visualElement.projection + : getClosestProjectingNode(visualElement.parent); +} + +export { useVisualElement }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/use-visual-state.mjs b/node_modules/framer-motion/dist/es/motion/utils/use-visual-state.mjs new file mode 100644 index 00000000..21cfad73 --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/use-visual-state.mjs @@ -0,0 +1,79 @@ +import { useContext } from 'react'; +import { isAnimationControls } from '../../animation/utils/is-animation-controls.mjs'; +import { MotionContext } from '../../context/MotionContext/index.mjs'; +import { PresenceContext } from '../../context/PresenceContext.mjs'; +import { isControllingVariants, isVariantNode } from '../../render/utils/is-controlling-variants.mjs'; +import { resolveVariantFromProps } from '../../render/utils/resolve-variants.mjs'; +import { useConstant } from '../../utils/use-constant.mjs'; +import { resolveMotionValue } from '../../value/utils/resolve-motion-value.mjs'; + +function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) { + const state = { + latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps), + renderState: createRenderState(), + }; + return state; +} +const makeUseVisualState = (config) => (props, isStatic) => { + const context = useContext(MotionContext); + const presenceContext = useContext(PresenceContext); + const make = () => makeState(config, props, context, presenceContext); + return isStatic ? make() : useConstant(make); +}; +function makeLatestValues(props, context, presenceContext, scrapeMotionValues) { + const values = {}; + const motionValues = scrapeMotionValues(props, {}); + for (const key in motionValues) { + values[key] = resolveMotionValue(motionValues[key]); + } + let { initial, animate } = props; + const isControllingVariants$1 = isControllingVariants(props); + const isVariantNode$1 = isVariantNode(props); + if (context && + isVariantNode$1 && + !isControllingVariants$1 && + props.inherit !== false) { + if (initial === undefined) + initial = context.initial; + if (animate === undefined) + animate = context.animate; + } + let isInitialAnimationBlocked = presenceContext + ? presenceContext.initial === false + : false; + isInitialAnimationBlocked = isInitialAnimationBlocked || initial === false; + const variantToSet = isInitialAnimationBlocked ? animate : initial; + if (variantToSet && + typeof variantToSet !== "boolean" && + !isAnimationControls(variantToSet)) { + const list = Array.isArray(variantToSet) ? variantToSet : [variantToSet]; + for (let i = 0; i < list.length; i++) { + const resolved = resolveVariantFromProps(props, list[i]); + if (resolved) { + const { transitionEnd, transition, ...target } = resolved; + for (const key in target) { + let valueTarget = target[key]; + if (Array.isArray(valueTarget)) { + /** + * Take final keyframe if the initial animation is blocked because + * we want to initialise at the end of that blocked animation. + */ + const index = isInitialAnimationBlocked + ? valueTarget.length - 1 + : 0; + valueTarget = valueTarget[index]; + } + if (valueTarget !== null) { + values[key] = valueTarget; + } + } + for (const key in transitionEnd) { + values[key] = transitionEnd[key]; + } + } + } + } + return values; +} + +export { makeUseVisualState }; diff --git a/node_modules/framer-motion/dist/es/motion/utils/valid-prop.mjs b/node_modules/framer-motion/dist/es/motion/utils/valid-prop.mjs new file mode 100644 index 00000000..d6587abe --- /dev/null +++ b/node_modules/framer-motion/dist/es/motion/utils/valid-prop.mjs @@ -0,0 +1,57 @@ +/** + * A list of all valid MotionProps. + * + * @privateRemarks + * This doesn't throw if a `MotionProp` name is missing - it should. + */ +const validMotionProps = new Set([ + "animate", + "exit", + "variants", + "initial", + "style", + "values", + "variants", + "transition", + "transformTemplate", + "custom", + "inherit", + "onBeforeLayoutMeasure", + "onAnimationStart", + "onAnimationComplete", + "onUpdate", + "onDragStart", + "onDrag", + "onDragEnd", + "onMeasureDragConstraints", + "onDirectionLock", + "onDragTransitionEnd", + "_dragX", + "_dragY", + "onHoverStart", + "onHoverEnd", + "onViewportEnter", + "onViewportLeave", + "globalTapTarget", + "ignoreStrict", + "viewport", +]); +/** + * Check whether a prop name is a valid `MotionProp` key. + * + * @param key - Name of the property to check + * @returns `true` is key is a valid `MotionProp`. + * + * @public + */ +function isValidMotionProp(key) { + return (key.startsWith("while") || + (key.startsWith("drag") && key !== "draggable") || + key.startsWith("layout") || + key.startsWith("onTap") || + key.startsWith("onPan") || + key.startsWith("onLayout") || + validMotionProps.has(key)); +} + +export { isValidMotionProp }; diff --git a/node_modules/framer-motion/dist/es/projection.mjs b/node_modules/framer-motion/dist/es/projection.mjs new file mode 100644 index 00000000..1ad606fc --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection.mjs @@ -0,0 +1,9 @@ +export { frame, frameData, mix, recordStats, statsBuffer } from 'motion-dom'; +export { calcBoxDelta } from './projection/geometry/delta-calc.mjs'; +export { nodeGroup } from './projection/node/group.mjs'; +export { HTMLProjectionNode } from './projection/node/HTMLProjectionNode.mjs'; +export { correctBorderRadius } from './projection/styles/scale-border-radius.mjs'; +export { correctBoxShadow } from './projection/styles/scale-box-shadow.mjs'; +export { addScaleCorrector } from './projection/styles/scale-correction.mjs'; +export { HTMLVisualElement } from './render/html/HTMLVisualElement.mjs'; +export { buildTransform } from './render/html/utils/build-transform.mjs'; diff --git a/node_modules/framer-motion/dist/es/projection/animation/mix-values.mjs b/node_modules/framer-motion/dist/es/projection/animation/mix-values.mjs new file mode 100644 index 00000000..a3f4db76 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/animation/mix-values.mjs @@ -0,0 +1,88 @@ +import { mixNumber, percent, px } from 'motion-dom'; +import { progress, circOut, noop } from 'motion-utils'; + +const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"]; +const numBorders = borders.length; +const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value; +const isPx = (value) => typeof value === "number" || px.test(value); +function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) { + if (shouldCrossfadeOpacity) { + target.opacity = mixNumber(0, lead.opacity ?? 1, easeCrossfadeIn(progress)); + target.opacityExit = mixNumber(follow.opacity ?? 1, 0, easeCrossfadeOut(progress)); + } + else if (isOnlyMember) { + target.opacity = mixNumber(follow.opacity ?? 1, lead.opacity ?? 1, progress); + } + /** + * Mix border radius + */ + for (let i = 0; i < numBorders; i++) { + const borderLabel = `border${borders[i]}Radius`; + let followRadius = getRadius(follow, borderLabel); + let leadRadius = getRadius(lead, borderLabel); + if (followRadius === undefined && leadRadius === undefined) + continue; + followRadius || (followRadius = 0); + leadRadius || (leadRadius = 0); + const canMix = followRadius === 0 || + leadRadius === 0 || + isPx(followRadius) === isPx(leadRadius); + if (canMix) { + target[borderLabel] = Math.max(mixNumber(asNumber(followRadius), asNumber(leadRadius), progress), 0); + if (percent.test(leadRadius) || percent.test(followRadius)) { + target[borderLabel] += "%"; + } + } + else { + target[borderLabel] = leadRadius; + } + } + /** + * Mix rotation + */ + if (follow.rotate || lead.rotate) { + target.rotate = mixNumber(follow.rotate || 0, lead.rotate || 0, progress); + } +} +function getRadius(values, radiusName) { + return values[radiusName] !== undefined + ? values[radiusName] + : values.borderRadius; +} +// /** +// * We only want to mix the background color if there's a follow element +// * that we're not crossfading opacity between. For instance with switch +// * AnimateSharedLayout animations, this helps the illusion of a continuous +// * element being animated but also cuts down on the number of paints triggered +// * for elements where opacity is doing that work for us. +// */ +// if ( +// !hasFollowElement && +// latestLeadValues.backgroundColor && +// latestFollowValues.backgroundColor +// ) { +// /** +// * This isn't ideal performance-wise as mixColor is creating a new function every frame. +// * We could probably create a mixer that runs at the start of the animation but +// * the idea behind the crossfader is that it runs dynamically between two potentially +// * changing targets (ie opacity or borderRadius may be animating independently via variants) +// */ +// leadState.backgroundColor = followState.backgroundColor = mixColor( +// latestFollowValues.backgroundColor as string, +// latestLeadValues.backgroundColor as string +// )(p) +// } +const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut); +const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop); +function compress(min, max, easing) { + return (p) => { + // Could replace ifs with clamp + if (p < min) + return 0; + if (p > max) + return 1; + return easing(progress(min, max, p)); + }; +} + +export { mixValues }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/conversion.mjs b/node_modules/framer-motion/dist/es/projection/geometry/conversion.mjs new file mode 100644 index 00000000..2188851c --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/conversion.mjs @@ -0,0 +1,33 @@ +/** + * Bounding boxes tend to be defined as top, left, right, bottom. For various operations + * it's easier to consider each axis individually. This function returns a bounding box + * as a map of single-axis min/max values. + */ +function convertBoundingBoxToBox({ top, left, right, bottom, }) { + return { + x: { min: left, max: right }, + y: { min: top, max: bottom }, + }; +} +function convertBoxToBoundingBox({ x, y }) { + return { top: y.min, right: x.max, bottom: y.max, left: x.min }; +} +/** + * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function + * provided by Framer to allow measured points to be corrected for device scaling. This is used + * when measuring DOM elements and DOM event points. + */ +function transformBoxPoints(point, transformPoint) { + if (!transformPoint) + return point; + const topLeft = transformPoint({ x: point.left, y: point.top }); + const bottomRight = transformPoint({ x: point.right, y: point.bottom }); + return { + top: topLeft.y, + left: topLeft.x, + bottom: bottomRight.y, + right: bottomRight.x, + }; +} + +export { convertBoundingBoxToBox, convertBoxToBoundingBox, transformBoxPoints }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/copy.mjs b/node_modules/framer-motion/dist/es/projection/geometry/copy.mjs new file mode 100644 index 00000000..210a880a --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/copy.mjs @@ -0,0 +1,31 @@ +/** + * Reset an axis to the provided origin box. + * + * This is a mutative operation. + */ +function copyAxisInto(axis, originAxis) { + axis.min = originAxis.min; + axis.max = originAxis.max; +} +/** + * Reset a box to the provided origin box. + * + * This is a mutative operation. + */ +function copyBoxInto(box, originBox) { + copyAxisInto(box.x, originBox.x); + copyAxisInto(box.y, originBox.y); +} +/** + * Reset a delta to the provided origin box. + * + * This is a mutative operation. + */ +function copyAxisDeltaInto(delta, originDelta) { + delta.translate = originDelta.translate; + delta.scale = originDelta.scale; + delta.originPoint = originDelta.originPoint; + delta.origin = originDelta.origin; +} + +export { copyAxisDeltaInto, copyAxisInto, copyBoxInto }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/delta-apply.mjs b/node_modules/framer-motion/dist/es/projection/geometry/delta-apply.mjs new file mode 100644 index 00000000..5e88625b --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/delta-apply.mjs @@ -0,0 +1,119 @@ +import { mixNumber } from 'motion-dom'; +import { hasTransform } from '../utils/has-transform.mjs'; + +/** + * Scales a point based on a factor and an originPoint + */ +function scalePoint(point, scale, originPoint) { + const distanceFromOrigin = point - originPoint; + const scaled = scale * distanceFromOrigin; + return originPoint + scaled; +} +/** + * Applies a translate/scale delta to a point + */ +function applyPointDelta(point, translate, scale, originPoint, boxScale) { + if (boxScale !== undefined) { + point = scalePoint(point, boxScale, originPoint); + } + return scalePoint(point, scale, originPoint) + translate; +} +/** + * Applies a translate/scale delta to an axis + */ +function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) { + axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale); + axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale); +} +/** + * Applies a translate/scale delta to a box + */ +function applyBoxDelta(box, { x, y }) { + applyAxisDelta(box.x, x.translate, x.scale, x.originPoint); + applyAxisDelta(box.y, y.translate, y.scale, y.originPoint); +} +const TREE_SCALE_SNAP_MIN = 0.999999999999; +const TREE_SCALE_SNAP_MAX = 1.0000000000001; +/** + * Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms + * in a tree upon our box before then calculating how to project it into our desired viewport-relative box + * + * This is the final nested loop within updateLayoutDelta for future refactoring + */ +function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) { + const treeLength = treePath.length; + if (!treeLength) + return; + // Reset the treeScale + treeScale.x = treeScale.y = 1; + let node; + let delta; + for (let i = 0; i < treeLength; i++) { + node = treePath[i]; + delta = node.projectionDelta; + /** + * TODO: Prefer to remove this, but currently we have motion components with + * display: contents in Framer. + */ + const { visualElement } = node.options; + if (visualElement && + visualElement.props.style && + visualElement.props.style.display === "contents") { + continue; + } + if (isSharedTransition && + node.options.layoutScroll && + node.scroll && + node !== node.root) { + transformBox(box, { + x: -node.scroll.offset.x, + y: -node.scroll.offset.y, + }); + } + if (delta) { + // Incoporate each ancestor's scale into a culmulative treeScale for this component + treeScale.x *= delta.x.scale; + treeScale.y *= delta.y.scale; + // Apply each ancestor's calculated delta into this component's recorded layout box + applyBoxDelta(box, delta); + } + if (isSharedTransition && hasTransform(node.latestValues)) { + transformBox(box, node.latestValues); + } + } + /** + * Snap tree scale back to 1 if it's within a non-perceivable threshold. + * This will help reduce useless scales getting rendered. + */ + if (treeScale.x < TREE_SCALE_SNAP_MAX && + treeScale.x > TREE_SCALE_SNAP_MIN) { + treeScale.x = 1.0; + } + if (treeScale.y < TREE_SCALE_SNAP_MAX && + treeScale.y > TREE_SCALE_SNAP_MIN) { + treeScale.y = 1.0; + } +} +function translateAxis(axis, distance) { + axis.min = axis.min + distance; + axis.max = axis.max + distance; +} +/** + * Apply a transform to an axis from the latest resolved motion values. + * This function basically acts as a bridge between a flat motion value map + * and applyAxisDelta + */ +function transformAxis(axis, axisTranslate, axisScale, boxScale, axisOrigin = 0.5) { + const originPoint = mixNumber(axis.min, axis.max, axisOrigin); + // Apply the axis delta to the final axis + applyAxisDelta(axis, axisTranslate, axisScale, originPoint, boxScale); +} +/** + * Apply a transform to a box from the latest resolved motion values. + */ +function transformBox(box, transform) { + transformAxis(box.x, transform.x, transform.scaleX, transform.scale, transform.originX); + transformAxis(box.y, transform.y, transform.scaleY, transform.scale, transform.originY); +} + +export { applyAxisDelta, applyBoxDelta, applyPointDelta, applyTreeDeltas, scalePoint, transformAxis, transformBox, translateAxis }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/delta-calc.mjs b/node_modules/framer-motion/dist/es/projection/geometry/delta-calc.mjs new file mode 100644 index 00000000..c0c1979c --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/delta-calc.mjs @@ -0,0 +1,52 @@ +import { mixNumber } from 'motion-dom'; + +const SCALE_PRECISION = 0.0001; +const SCALE_MIN = 1 - SCALE_PRECISION; +const SCALE_MAX = 1 + SCALE_PRECISION; +const TRANSLATE_PRECISION = 0.01; +const TRANSLATE_MIN = 0 - TRANSLATE_PRECISION; +const TRANSLATE_MAX = 0 + TRANSLATE_PRECISION; +function calcLength(axis) { + return axis.max - axis.min; +} +function isNear(value, target, maxDistance) { + return Math.abs(value - target) <= maxDistance; +} +function calcAxisDelta(delta, source, target, origin = 0.5) { + delta.origin = origin; + delta.originPoint = mixNumber(source.min, source.max, delta.origin); + delta.scale = calcLength(target) / calcLength(source); + delta.translate = + mixNumber(target.min, target.max, delta.origin) - delta.originPoint; + if ((delta.scale >= SCALE_MIN && delta.scale <= SCALE_MAX) || + isNaN(delta.scale)) { + delta.scale = 1.0; + } + if ((delta.translate >= TRANSLATE_MIN && + delta.translate <= TRANSLATE_MAX) || + isNaN(delta.translate)) { + delta.translate = 0.0; + } +} +function calcBoxDelta(delta, source, target, origin) { + calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined); + calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined); +} +function calcRelativeAxis(target, relative, parent) { + target.min = parent.min + relative.min; + target.max = target.min + calcLength(relative); +} +function calcRelativeBox(target, relative, parent) { + calcRelativeAxis(target.x, relative.x, parent.x); + calcRelativeAxis(target.y, relative.y, parent.y); +} +function calcRelativeAxisPosition(target, layout, parent) { + target.min = layout.min - parent.min; + target.max = target.min + calcLength(layout); +} +function calcRelativePosition(target, layout, parent) { + calcRelativeAxisPosition(target.x, layout.x, parent.x); + calcRelativeAxisPosition(target.y, layout.y, parent.y); +} + +export { calcAxisDelta, calcBoxDelta, calcLength, calcRelativeAxis, calcRelativeAxisPosition, calcRelativeBox, calcRelativePosition, isNear }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/delta-remove.mjs b/node_modules/framer-motion/dist/es/projection/geometry/delta-remove.mjs new file mode 100644 index 00000000..7d2ad58f --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/delta-remove.mjs @@ -0,0 +1,53 @@ +import { percent, mixNumber } from 'motion-dom'; +import { scalePoint } from './delta-apply.mjs'; + +/** + * Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse + */ +function removePointDelta(point, translate, scale, originPoint, boxScale) { + point -= translate; + point = scalePoint(point, 1 / scale, originPoint); + if (boxScale !== undefined) { + point = scalePoint(point, 1 / boxScale, originPoint); + } + return point; +} +/** + * Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse + */ +function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) { + if (percent.test(translate)) { + translate = parseFloat(translate); + const relativeProgress = mixNumber(sourceAxis.min, sourceAxis.max, translate / 100); + translate = relativeProgress - sourceAxis.min; + } + if (typeof translate !== "number") + return; + let originPoint = mixNumber(originAxis.min, originAxis.max, origin); + if (axis === originAxis) + originPoint -= translate; + axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale); + axis.max = removePointDelta(axis.max, translate, scale, originPoint, boxScale); +} +/** + * Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse + * and acts as a bridge between motion values and removeAxisDelta + */ +function removeAxisTransforms(axis, transforms, [key, scaleKey, originKey], origin, sourceAxis) { + removeAxisDelta(axis, transforms[key], transforms[scaleKey], transforms[originKey], transforms.scale, origin, sourceAxis); +} +/** + * The names of the motion values we want to apply as translation, scale and origin. + */ +const xKeys = ["x", "scaleX", "originX"]; +const yKeys = ["y", "scaleY", "originY"]; +/** + * Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse + * and acts as a bridge between motion values and removeAxisDelta + */ +function removeBoxTransforms(box, transforms, originBox, sourceBox) { + removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined); + removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined); +} + +export { removeAxisDelta, removeAxisTransforms, removeBoxTransforms, removePointDelta }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/models.mjs b/node_modules/framer-motion/dist/es/projection/geometry/models.mjs new file mode 100644 index 00000000..5e01de0a --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/models.mjs @@ -0,0 +1,17 @@ +const createAxisDelta = () => ({ + translate: 0, + scale: 1, + origin: 0, + originPoint: 0, +}); +const createDelta = () => ({ + x: createAxisDelta(), + y: createAxisDelta(), +}); +const createAxis = () => ({ min: 0, max: 0 }); +const createBox = () => ({ + x: createAxis(), + y: createAxis(), +}); + +export { createAxis, createAxisDelta, createBox, createDelta }; diff --git a/node_modules/framer-motion/dist/es/projection/geometry/utils.mjs b/node_modules/framer-motion/dist/es/projection/geometry/utils.mjs new file mode 100644 index 00000000..d2a78edf --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/geometry/utils.mjs @@ -0,0 +1,31 @@ +import { calcLength } from './delta-calc.mjs'; + +function isAxisDeltaZero(delta) { + return delta.translate === 0 && delta.scale === 1; +} +function isDeltaZero(delta) { + return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y); +} +function axisEquals(a, b) { + return a.min === b.min && a.max === b.max; +} +function boxEquals(a, b) { + return axisEquals(a.x, b.x) && axisEquals(a.y, b.y); +} +function axisEqualsRounded(a, b) { + return (Math.round(a.min) === Math.round(b.min) && + Math.round(a.max) === Math.round(b.max)); +} +function boxEqualsRounded(a, b) { + return axisEqualsRounded(a.x, b.x) && axisEqualsRounded(a.y, b.y); +} +function aspectRatio(box) { + return calcLength(box.x) / calcLength(box.y); +} +function axisDeltaEquals(a, b) { + return (a.translate === b.translate && + a.scale === b.scale && + a.originPoint === b.originPoint); +} + +export { aspectRatio, axisDeltaEquals, axisEquals, axisEqualsRounded, boxEquals, boxEqualsRounded, isDeltaZero }; diff --git a/node_modules/framer-motion/dist/es/projection/node/DocumentProjectionNode.mjs b/node_modules/framer-motion/dist/es/projection/node/DocumentProjectionNode.mjs new file mode 100644 index 00000000..7f1a4234 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/node/DocumentProjectionNode.mjs @@ -0,0 +1,13 @@ +import { addDomEvent } from '../../events/add-dom-event.mjs'; +import { createProjectionNode } from './create-projection-node.mjs'; + +const DocumentProjectionNode = createProjectionNode({ + attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify), + measureScroll: () => ({ + x: document.documentElement.scrollLeft || document.body.scrollLeft, + y: document.documentElement.scrollTop || document.body.scrollTop, + }), + checkIsScrollRoot: () => true, +}); + +export { DocumentProjectionNode }; diff --git a/node_modules/framer-motion/dist/es/projection/node/HTMLProjectionNode.mjs b/node_modules/framer-motion/dist/es/projection/node/HTMLProjectionNode.mjs new file mode 100644 index 00000000..95f86466 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/node/HTMLProjectionNode.mjs @@ -0,0 +1,27 @@ +import { createProjectionNode } from './create-projection-node.mjs'; +import { DocumentProjectionNode } from './DocumentProjectionNode.mjs'; + +const rootProjectionNode = { + current: undefined, +}; +const HTMLProjectionNode = createProjectionNode({ + measureScroll: (instance) => ({ + x: instance.scrollLeft, + y: instance.scrollTop, + }), + defaultParent: () => { + if (!rootProjectionNode.current) { + const documentNode = new DocumentProjectionNode({}); + documentNode.mount(window); + documentNode.setOptions({ layoutScroll: true }); + rootProjectionNode.current = documentNode; + } + return rootProjectionNode.current; + }, + resetTransform: (instance, value) => { + instance.style.transform = value !== undefined ? value : "none"; + }, + checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"), +}); + +export { HTMLProjectionNode, rootProjectionNode }; diff --git a/node_modules/framer-motion/dist/es/projection/node/create-projection-node.mjs b/node_modules/framer-motion/dist/es/projection/node/create-projection-node.mjs new file mode 100644 index 00000000..50a7dabd --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/node/create-projection-node.mjs @@ -0,0 +1,1612 @@ +import { statsBuffer, isSVGElement, isSVGSVGElement, frame, getValueTransition, cancelFrame, time, frameData, frameSteps, microtask, activeAnimations, motionValue, mixNumber } from 'motion-dom'; +import { SubscriptionManager, clamp, noop } from 'motion-utils'; +import { animateSingleValue } from '../../animation/animate/single-value.mjs'; +import { getOptimisedAppearId } from '../../animation/optimized-appear/get-appear-id.mjs'; +import { FlatTree } from '../../render/utils/flat-tree.mjs'; +import { delay } from '../../utils/delay.mjs'; +import { resolveMotionValue } from '../../value/utils/resolve-motion-value.mjs'; +import { mixValues } from '../animation/mix-values.mjs'; +import { copyBoxInto, copyAxisDeltaInto } from '../geometry/copy.mjs'; +import { translateAxis, transformBox, applyBoxDelta, applyTreeDeltas } from '../geometry/delta-apply.mjs'; +import { calcLength, calcRelativePosition, calcRelativeBox, calcBoxDelta, isNear } from '../geometry/delta-calc.mjs'; +import { removeBoxTransforms } from '../geometry/delta-remove.mjs'; +import { createBox, createDelta } from '../geometry/models.mjs'; +import { boxEqualsRounded, isDeltaZero, axisDeltaEquals, aspectRatio, boxEquals } from '../geometry/utils.mjs'; +import { NodeStack } from '../shared/stack.mjs'; +import { scaleCorrectors } from '../styles/scale-correction.mjs'; +import { buildProjectionTransform } from '../styles/transform.mjs'; +import { eachAxis } from '../utils/each-axis.mjs'; +import { hasTransform, hasScale, has2DTranslate } from '../utils/has-transform.mjs'; +import { globalProjectionState } from './state.mjs'; + +const metrics = { + nodes: 0, + calculatedTargetDeltas: 0, + calculatedProjections: 0, +}; +const transformAxes = ["", "X", "Y", "Z"]; +/** + * We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1 + * which has a noticeable difference in spring animations + */ +const animationTarget = 1000; +let id = 0; +function resetDistortingTransform(key, visualElement, values, sharedAnimationValues) { + const { latestValues } = visualElement; + // Record the distorting transform and then temporarily set it to 0 + if (latestValues[key]) { + values[key] = latestValues[key]; + visualElement.setStaticValue(key, 0); + if (sharedAnimationValues) { + sharedAnimationValues[key] = 0; + } + } +} +function cancelTreeOptimisedTransformAnimations(projectionNode) { + projectionNode.hasCheckedOptimisedAppear = true; + if (projectionNode.root === projectionNode) + return; + const { visualElement } = projectionNode.options; + if (!visualElement) + return; + const appearId = getOptimisedAppearId(visualElement); + if (window.MotionHasOptimisedAnimation(appearId, "transform")) { + const { layout, layoutId } = projectionNode.options; + window.MotionCancelOptimisedAnimation(appearId, "transform", frame, !(layout || layoutId)); + } + const { parent } = projectionNode; + if (parent && !parent.hasCheckedOptimisedAppear) { + cancelTreeOptimisedTransformAnimations(parent); + } +} +function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) { + return class ProjectionNode { + constructor(latestValues = {}, parent = defaultParent?.()) { + /** + * A unique ID generated for every projection node. + */ + this.id = id++; + /** + * An id that represents a unique session instigated by startUpdate. + */ + this.animationId = 0; + this.animationCommitId = 0; + /** + * A Set containing all this component's children. This is used to iterate + * through the children. + * + * TODO: This could be faster to iterate as a flat array stored on the root node. + */ + this.children = new Set(); + /** + * Options for the node. We use this to configure what kind of layout animations + * we should perform (if any). + */ + this.options = {}; + /** + * We use this to detect when its safe to shut down part of a projection tree. + * We have to keep projecting children for scale correction and relative projection + * until all their parents stop performing layout animations. + */ + this.isTreeAnimating = false; + this.isAnimationBlocked = false; + /** + * Flag to true if we think this layout has been changed. We can't always know this, + * currently we set it to true every time a component renders, or if it has a layoutDependency + * if that has changed between renders. Additionally, components can be grouped by LayoutGroup + * and if one node is dirtied, they all are. + */ + this.isLayoutDirty = false; + /** + * Flag to true if we think the projection calculations for this node needs + * recalculating as a result of an updated transform or layout animation. + */ + this.isProjectionDirty = false; + /** + * Flag to true if the layout *or* transform has changed. This then gets propagated + * throughout the projection tree, forcing any element below to recalculate on the next frame. + */ + this.isSharedProjectionDirty = false; + /** + * Flag transform dirty. This gets propagated throughout the whole tree but is only + * respected by shared nodes. + */ + this.isTransformDirty = false; + /** + * Block layout updates for instant layout transitions throughout the tree. + */ + this.updateManuallyBlocked = false; + this.updateBlockedByResize = false; + /** + * Set to true between the start of the first `willUpdate` call and the end of the `didUpdate` + * call. + */ + this.isUpdating = false; + /** + * If this is an SVG element we currently disable projection transforms + */ + this.isSVG = false; + /** + * Flag to true (during promotion) if a node doing an instant layout transition needs to reset + * its projection styles. + */ + this.needsReset = false; + /** + * Flags whether this node should have its transform reset prior to measuring. + */ + this.shouldResetTransform = false; + /** + * Store whether this node has been checked for optimised appear animations. As + * effects fire bottom-up, and we want to look up the tree for appear animations, + * this makes sure we only check each path once, stopping at nodes that + * have already been checked. + */ + this.hasCheckedOptimisedAppear = false; + /** + * An object representing the calculated contextual/accumulated/tree scale. + * This will be used to scale calculcated projection transforms, as these are + * calculated in screen-space but need to be scaled for elements to layoutly + * make it to their calculated destinations. + * + * TODO: Lazy-init + */ + this.treeScale = { x: 1, y: 1 }; + /** + * + */ + this.eventHandlers = new Map(); + this.hasTreeAnimated = false; + // Note: Currently only running on root node + this.updateScheduled = false; + this.scheduleUpdate = () => this.update(); + this.projectionUpdateScheduled = false; + this.checkUpdateFailed = () => { + if (this.isUpdating) { + this.isUpdating = false; + this.clearAllSnapshots(); + } + }; + /** + * This is a multi-step process as shared nodes might be of different depths. Nodes + * are sorted by depth order, so we need to resolve the entire tree before moving to + * the next step. + */ + this.updateProjection = () => { + this.projectionUpdateScheduled = false; + /** + * Reset debug counts. Manually resetting rather than creating a new + * object each frame. + */ + if (statsBuffer.value) { + metrics.nodes = + metrics.calculatedTargetDeltas = + metrics.calculatedProjections = + 0; + } + this.nodes.forEach(propagateDirtyNodes); + this.nodes.forEach(resolveTargetDelta); + this.nodes.forEach(calcProjection); + this.nodes.forEach(cleanDirtyNodes); + if (statsBuffer.addProjectionMetrics) { + statsBuffer.addProjectionMetrics(metrics); + } + }; + /** + * Frame calculations + */ + this.resolvedRelativeTargetAt = 0.0; + this.hasProjected = false; + this.isVisible = true; + this.animationProgress = 0; + /** + * Shared layout + */ + // TODO Only running on root node + this.sharedNodes = new Map(); + this.latestValues = latestValues; + this.root = parent ? parent.root || parent : this; + this.path = parent ? [...parent.path, parent] : []; + this.parent = parent; + this.depth = parent ? parent.depth + 1 : 0; + for (let i = 0; i < this.path.length; i++) { + this.path[i].shouldResetTransform = true; + } + if (this.root === this) + this.nodes = new FlatTree(); + } + addEventListener(name, handler) { + if (!this.eventHandlers.has(name)) { + this.eventHandlers.set(name, new SubscriptionManager()); + } + return this.eventHandlers.get(name).add(handler); + } + notifyListeners(name, ...args) { + const subscriptionManager = this.eventHandlers.get(name); + subscriptionManager && subscriptionManager.notify(...args); + } + hasListeners(name) { + return this.eventHandlers.has(name); + } + /** + * Lifecycles + */ + mount(instance) { + if (this.instance) + return; + this.isSVG = isSVGElement(instance) && !isSVGSVGElement(instance); + this.instance = instance; + const { layoutId, layout, visualElement } = this.options; + if (visualElement && !visualElement.current) { + visualElement.mount(instance); + } + this.root.nodes.add(this); + this.parent && this.parent.children.add(this); + if (this.root.hasTreeAnimated && (layout || layoutId)) { + this.isLayoutDirty = true; + } + if (attachResizeListener) { + let cancelDelay; + let innerWidth = 0; + const resizeUnblockUpdate = () => (this.root.updateBlockedByResize = false); + // Set initial innerWidth in a frame.read callback to batch the read + frame.read(() => { + innerWidth = window.innerWidth; + }); + attachResizeListener(instance, () => { + const newInnerWidth = window.innerWidth; + if (newInnerWidth === innerWidth) + return; + innerWidth = newInnerWidth; + this.root.updateBlockedByResize = true; + cancelDelay && cancelDelay(); + cancelDelay = delay(resizeUnblockUpdate, 250); + if (globalProjectionState.hasAnimatedSinceResize) { + globalProjectionState.hasAnimatedSinceResize = false; + this.nodes.forEach(finishAnimation); + } + }); + } + if (layoutId) { + this.root.registerSharedNode(layoutId, this); + } + // Only register the handler if it requires layout animation + if (this.options.animate !== false && + visualElement && + (layoutId || layout)) { + this.addEventListener("didUpdate", ({ delta, hasLayoutChanged, hasRelativeLayoutChanged, layout: newLayout, }) => { + if (this.isTreeAnimationBlocked()) { + this.target = undefined; + this.relativeTarget = undefined; + return; + } + // TODO: Check here if an animation exists + const layoutTransition = this.options.transition || + visualElement.getDefaultTransition() || + defaultLayoutTransition; + const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps(); + /** + * The target layout of the element might stay the same, + * but its position relative to its parent has changed. + */ + const hasTargetChanged = !this.targetLayout || + !boxEqualsRounded(this.targetLayout, newLayout); + /* + * Note: Disabled to fix relative animations always triggering new + * layout animations. If this causes further issues, we can try + * a different approach to detecting relative target changes. + */ + // || hasRelativeLayoutChanged + /** + * If the layout hasn't seemed to have changed, it might be that the + * element is visually in the same place in the document but its position + * relative to its parent has indeed changed. So here we check for that. + */ + const hasOnlyRelativeTargetChanged = !hasLayoutChanged && hasRelativeLayoutChanged; + if (this.options.layoutRoot || + this.resumeFrom || + hasOnlyRelativeTargetChanged || + (hasLayoutChanged && + (hasTargetChanged || !this.currentAnimation))) { + if (this.resumeFrom) { + this.resumingFrom = this.resumeFrom; + this.resumingFrom.resumingFrom = undefined; + } + const animationOptions = { + ...getValueTransition(layoutTransition, "layout"), + onPlay: onLayoutAnimationStart, + onComplete: onLayoutAnimationComplete, + }; + if (visualElement.shouldReduceMotion || + this.options.layoutRoot) { + animationOptions.delay = 0; + animationOptions.type = false; + } + this.startAnimation(animationOptions); + /** + * Set animation origin after starting animation to avoid layout jump + * caused by stopping previous layout animation + */ + this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged); + } + else { + /** + * If the layout hasn't changed and we have an animation that hasn't started yet, + * finish it immediately. Otherwise it will be animating from a location + * that was probably never commited to screen and look like a jumpy box. + */ + if (!hasLayoutChanged) { + finishAnimation(this); + } + if (this.isLead() && this.options.onExitComplete) { + this.options.onExitComplete(); + } + } + this.targetLayout = newLayout; + }); + } + } + unmount() { + this.options.layoutId && this.willUpdate(); + this.root.nodes.remove(this); + const stack = this.getStack(); + stack && stack.remove(this); + this.parent && this.parent.children.delete(this); + this.instance = undefined; + this.eventHandlers.clear(); + cancelFrame(this.updateProjection); + } + // only on the root + blockUpdate() { + this.updateManuallyBlocked = true; + } + unblockUpdate() { + this.updateManuallyBlocked = false; + } + isUpdateBlocked() { + return this.updateManuallyBlocked || this.updateBlockedByResize; + } + isTreeAnimationBlocked() { + return (this.isAnimationBlocked || + (this.parent && this.parent.isTreeAnimationBlocked()) || + false); + } + // Note: currently only running on root node + startUpdate() { + if (this.isUpdateBlocked()) + return; + this.isUpdating = true; + this.nodes && this.nodes.forEach(resetSkewAndRotation); + this.animationId++; + } + getTransformTemplate() { + const { visualElement } = this.options; + return visualElement && visualElement.getProps().transformTemplate; + } + willUpdate(shouldNotifyListeners = true) { + this.root.hasTreeAnimated = true; + if (this.root.isUpdateBlocked()) { + this.options.onExitComplete && this.options.onExitComplete(); + return; + } + /** + * If we're running optimised appear animations then these must be + * cancelled before measuring the DOM. This is so we can measure + * the true layout of the element rather than the WAAPI animation + * which will be unaffected by the resetSkewAndRotate step. + * + * Note: This is a DOM write. Worst case scenario is this is sandwiched + * between other snapshot reads which will cause unnecessary style recalculations. + * This has to happen here though, as we don't yet know which nodes will need + * snapshots in startUpdate(), but we only want to cancel optimised animations + * if a layout animation measurement is actually going to be affected by them. + */ + if (window.MotionCancelOptimisedAnimation && + !this.hasCheckedOptimisedAppear) { + cancelTreeOptimisedTransformAnimations(this); + } + !this.root.isUpdating && this.root.startUpdate(); + if (this.isLayoutDirty) + return; + this.isLayoutDirty = true; + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + node.shouldResetTransform = true; + node.updateScroll("snapshot"); + if (node.options.layoutRoot) { + node.willUpdate(false); + } + } + const { layoutId, layout } = this.options; + if (layoutId === undefined && !layout) + return; + const transformTemplate = this.getTransformTemplate(); + this.prevTransformTemplateValue = transformTemplate + ? transformTemplate(this.latestValues, "") + : undefined; + this.updateSnapshot(); + shouldNotifyListeners && this.notifyListeners("willUpdate"); + } + update() { + this.updateScheduled = false; + const updateWasBlocked = this.isUpdateBlocked(); + // When doing an instant transition, we skip the layout update, + // but should still clean up the measurements so that the next + // snapshot could be taken correctly. + if (updateWasBlocked) { + this.unblockUpdate(); + this.clearAllSnapshots(); + this.nodes.forEach(clearMeasurements); + return; + } + /** + * If this is a repeat of didUpdate then ignore the animation. + */ + if (this.animationId <= this.animationCommitId) { + this.nodes.forEach(clearIsLayoutDirty); + return; + } + this.animationCommitId = this.animationId; + if (!this.isUpdating) { + this.nodes.forEach(clearIsLayoutDirty); + } + else { + this.isUpdating = false; + /** + * Write + */ + this.nodes.forEach(resetTransformStyle); + /** + * Read ================== + */ + // Update layout measurements of updated children + this.nodes.forEach(updateLayout); + /** + * Write + */ + // Notify listeners that the layout is updated + this.nodes.forEach(notifyLayoutUpdate); + } + this.clearAllSnapshots(); + /** + * Manually flush any pending updates. Ideally + * we could leave this to the following requestAnimationFrame but this seems + * to leave a flash of incorrectly styled content. + */ + const now = time.now(); + frameData.delta = clamp(0, 1000 / 60, now - frameData.timestamp); + frameData.timestamp = now; + frameData.isProcessing = true; + frameSteps.update.process(frameData); + frameSteps.preRender.process(frameData); + frameSteps.render.process(frameData); + frameData.isProcessing = false; + } + didUpdate() { + if (!this.updateScheduled) { + this.updateScheduled = true; + microtask.read(this.scheduleUpdate); + } + } + clearAllSnapshots() { + this.nodes.forEach(clearSnapshot); + this.sharedNodes.forEach(removeLeadSnapshots); + } + scheduleUpdateProjection() { + if (!this.projectionUpdateScheduled) { + this.projectionUpdateScheduled = true; + frame.preRender(this.updateProjection, false, true); + } + } + scheduleCheckAfterUnmount() { + /** + * If the unmounting node is in a layoutGroup and did trigger a willUpdate, + * we manually call didUpdate to give a chance to the siblings to animate. + * Otherwise, cleanup all snapshots to prevents future nodes from reusing them. + */ + frame.postRender(() => { + if (this.isLayoutDirty) { + this.root.didUpdate(); + } + else { + this.root.checkUpdateFailed(); + } + }); + } + /** + * Update measurements + */ + updateSnapshot() { + if (this.snapshot || !this.instance) + return; + this.snapshot = this.measure(); + if (this.snapshot && + !calcLength(this.snapshot.measuredBox.x) && + !calcLength(this.snapshot.measuredBox.y)) { + this.snapshot = undefined; + } + } + updateLayout() { + if (!this.instance) + return; + this.updateScroll(); + if (!(this.options.alwaysMeasureLayout && this.isLead()) && + !this.isLayoutDirty) { + return; + } + /** + * When a node is mounted, it simply resumes from the prevLead's + * snapshot instead of taking a new one, but the ancestors scroll + * might have updated while the prevLead is unmounted. We need to + * update the scroll again to make sure the layout we measure is + * up to date. + */ + if (this.resumeFrom && !this.resumeFrom.instance) { + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + node.updateScroll(); + } + } + const prevLayout = this.layout; + this.layout = this.measure(false); + this.layoutCorrected = createBox(); + this.isLayoutDirty = false; + this.projectionDelta = undefined; + this.notifyListeners("measure", this.layout.layoutBox); + const { visualElement } = this.options; + visualElement && + visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined); + } + updateScroll(phase = "measure") { + let needsMeasurement = Boolean(this.options.layoutScroll && this.instance); + if (this.scroll && + this.scroll.animationId === this.root.animationId && + this.scroll.phase === phase) { + needsMeasurement = false; + } + if (needsMeasurement && this.instance) { + const isRoot = checkIsScrollRoot(this.instance); + this.scroll = { + animationId: this.root.animationId, + phase, + isRoot, + offset: measureScroll(this.instance), + wasRoot: this.scroll ? this.scroll.isRoot : isRoot, + }; + } + } + resetTransform() { + if (!resetTransform) + return; + const isResetRequested = this.isLayoutDirty || + this.shouldResetTransform || + this.options.alwaysMeasureLayout; + const hasProjection = this.projectionDelta && !isDeltaZero(this.projectionDelta); + const transformTemplate = this.getTransformTemplate(); + const transformTemplateValue = transformTemplate + ? transformTemplate(this.latestValues, "") + : undefined; + const transformTemplateHasChanged = transformTemplateValue !== this.prevTransformTemplateValue; + if (isResetRequested && + this.instance && + (hasProjection || + hasTransform(this.latestValues) || + transformTemplateHasChanged)) { + resetTransform(this.instance, transformTemplateValue); + this.shouldResetTransform = false; + this.scheduleRender(); + } + } + measure(removeTransform = true) { + const pageBox = this.measurePageBox(); + let layoutBox = this.removeElementScroll(pageBox); + /** + * Measurements taken during the pre-render stage + * still have transforms applied so we remove them + * via calculation. + */ + if (removeTransform) { + layoutBox = this.removeTransform(layoutBox); + } + roundBox(layoutBox); + return { + animationId: this.root.animationId, + measuredBox: pageBox, + layoutBox, + latestValues: {}, + source: this.id, + }; + } + measurePageBox() { + const { visualElement } = this.options; + if (!visualElement) + return createBox(); + const box = visualElement.measureViewportBox(); + const wasInScrollRoot = this.scroll?.wasRoot || this.path.some(checkNodeWasScrollRoot); + if (!wasInScrollRoot) { + // Remove viewport scroll to give page-relative coordinates + const { scroll } = this.root; + if (scroll) { + translateAxis(box.x, scroll.offset.x); + translateAxis(box.y, scroll.offset.y); + } + } + return box; + } + removeElementScroll(box) { + const boxWithoutScroll = createBox(); + copyBoxInto(boxWithoutScroll, box); + if (this.scroll?.wasRoot) { + return boxWithoutScroll; + } + /** + * Performance TODO: Keep a cumulative scroll offset down the tree + * rather than loop back up the path. + */ + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + const { scroll, options } = node; + if (node !== this.root && scroll && options.layoutScroll) { + /** + * If this is a new scroll root, we want to remove all previous scrolls + * from the viewport box. + */ + if (scroll.wasRoot) { + copyBoxInto(boxWithoutScroll, box); + } + translateAxis(boxWithoutScroll.x, scroll.offset.x); + translateAxis(boxWithoutScroll.y, scroll.offset.y); + } + } + return boxWithoutScroll; + } + applyTransform(box, transformOnly = false) { + const withTransforms = createBox(); + copyBoxInto(withTransforms, box); + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + if (!transformOnly && + node.options.layoutScroll && + node.scroll && + node !== node.root) { + transformBox(withTransforms, { + x: -node.scroll.offset.x, + y: -node.scroll.offset.y, + }); + } + if (!hasTransform(node.latestValues)) + continue; + transformBox(withTransforms, node.latestValues); + } + if (hasTransform(this.latestValues)) { + transformBox(withTransforms, this.latestValues); + } + return withTransforms; + } + removeTransform(box) { + const boxWithoutTransform = createBox(); + copyBoxInto(boxWithoutTransform, box); + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + if (!node.instance) + continue; + if (!hasTransform(node.latestValues)) + continue; + hasScale(node.latestValues) && node.updateSnapshot(); + const sourceBox = createBox(); + const nodeBox = node.measurePageBox(); + copyBoxInto(sourceBox, nodeBox); + removeBoxTransforms(boxWithoutTransform, node.latestValues, node.snapshot ? node.snapshot.layoutBox : undefined, sourceBox); + } + if (hasTransform(this.latestValues)) { + removeBoxTransforms(boxWithoutTransform, this.latestValues); + } + return boxWithoutTransform; + } + setTargetDelta(delta) { + this.targetDelta = delta; + this.root.scheduleUpdateProjection(); + this.isProjectionDirty = true; + } + setOptions(options) { + this.options = { + ...this.options, + ...options, + crossfade: options.crossfade !== undefined ? options.crossfade : true, + }; + } + clearMeasurements() { + this.scroll = undefined; + this.layout = undefined; + this.snapshot = undefined; + this.prevTransformTemplateValue = undefined; + this.targetDelta = undefined; + this.target = undefined; + this.isLayoutDirty = false; + } + forceRelativeParentToResolveTarget() { + if (!this.relativeParent) + return; + /** + * If the parent target isn't up-to-date, force it to update. + * This is an unfortunate de-optimisation as it means any updating relative + * projection will cause all the relative parents to recalculate back + * up the tree. + */ + if (this.relativeParent.resolvedRelativeTargetAt !== + frameData.timestamp) { + this.relativeParent.resolveTargetDelta(true); + } + } + resolveTargetDelta(forceRecalculation = false) { + /** + * Once the dirty status of nodes has been spread through the tree, we also + * need to check if we have a shared node of a different depth that has itself + * been dirtied. + */ + const lead = this.getLead(); + this.isProjectionDirty || (this.isProjectionDirty = lead.isProjectionDirty); + this.isTransformDirty || (this.isTransformDirty = lead.isTransformDirty); + this.isSharedProjectionDirty || (this.isSharedProjectionDirty = lead.isSharedProjectionDirty); + const isShared = Boolean(this.resumingFrom) || this !== lead; + /** + * We don't use transform for this step of processing so we don't + * need to check whether any nodes have changed transform. + */ + const canSkip = !(forceRecalculation || + (isShared && this.isSharedProjectionDirty) || + this.isProjectionDirty || + this.parent?.isProjectionDirty || + this.attemptToResolveRelativeTarget || + this.root.updateBlockedByResize); + if (canSkip) + return; + const { layout, layoutId } = this.options; + /** + * If we have no layout, we can't perform projection, so early return + */ + if (!this.layout || !(layout || layoutId)) + return; + this.resolvedRelativeTargetAt = frameData.timestamp; + /** + * If we don't have a targetDelta but do have a layout, we can attempt to resolve + * a relativeParent. This will allow a component to perform scale correction + * even if no animation has started. + */ + if (!this.targetDelta && !this.relativeTarget) { + const relativeParent = this.getClosestProjectingParent(); + if (relativeParent && + relativeParent.layout && + this.animationProgress !== 1) { + this.relativeParent = relativeParent; + this.forceRelativeParentToResolveTarget(); + this.relativeTarget = createBox(); + this.relativeTargetOrigin = createBox(); + calcRelativePosition(this.relativeTargetOrigin, this.layout.layoutBox, relativeParent.layout.layoutBox); + copyBoxInto(this.relativeTarget, this.relativeTargetOrigin); + } + else { + this.relativeParent = this.relativeTarget = undefined; + } + } + /** + * If we have no relative target or no target delta our target isn't valid + * for this frame. + */ + if (!this.relativeTarget && !this.targetDelta) + return; + /** + * Lazy-init target data structure + */ + if (!this.target) { + this.target = createBox(); + this.targetWithTransforms = createBox(); + } + /** + * If we've got a relative box for this component, resolve it into a target relative to the parent. + */ + if (this.relativeTarget && + this.relativeTargetOrigin && + this.relativeParent && + this.relativeParent.target) { + this.forceRelativeParentToResolveTarget(); + calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target); + /** + * If we've only got a targetDelta, resolve it into a target + */ + } + else if (this.targetDelta) { + if (Boolean(this.resumingFrom)) { + // TODO: This is creating a new object every frame + this.target = this.applyTransform(this.layout.layoutBox); + } + else { + copyBoxInto(this.target, this.layout.layoutBox); + } + applyBoxDelta(this.target, this.targetDelta); + } + else { + /** + * If no target, use own layout as target + */ + copyBoxInto(this.target, this.layout.layoutBox); + } + /** + * If we've been told to attempt to resolve a relative target, do so. + */ + if (this.attemptToResolveRelativeTarget) { + this.attemptToResolveRelativeTarget = false; + const relativeParent = this.getClosestProjectingParent(); + if (relativeParent && + Boolean(relativeParent.resumingFrom) === + Boolean(this.resumingFrom) && + !relativeParent.options.layoutScroll && + relativeParent.target && + this.animationProgress !== 1) { + this.relativeParent = relativeParent; + this.forceRelativeParentToResolveTarget(); + this.relativeTarget = createBox(); + this.relativeTargetOrigin = createBox(); + calcRelativePosition(this.relativeTargetOrigin, this.target, relativeParent.target); + copyBoxInto(this.relativeTarget, this.relativeTargetOrigin); + } + else { + this.relativeParent = this.relativeTarget = undefined; + } + } + /** + * Increase debug counter for resolved target deltas + */ + if (statsBuffer.value) { + metrics.calculatedTargetDeltas++; + } + } + getClosestProjectingParent() { + if (!this.parent || + hasScale(this.parent.latestValues) || + has2DTranslate(this.parent.latestValues)) { + return undefined; + } + if (this.parent.isProjecting()) { + return this.parent; + } + else { + return this.parent.getClosestProjectingParent(); + } + } + isProjecting() { + return Boolean((this.relativeTarget || + this.targetDelta || + this.options.layoutRoot) && + this.layout); + } + calcProjection() { + const lead = this.getLead(); + const isShared = Boolean(this.resumingFrom) || this !== lead; + let canSkip = true; + /** + * If this is a normal layout animation and neither this node nor its nearest projecting + * is dirty then we can't skip. + */ + if (this.isProjectionDirty || this.parent?.isProjectionDirty) { + canSkip = false; + } + /** + * If this is a shared layout animation and this node's shared projection is dirty then + * we can't skip. + */ + if (isShared && + (this.isSharedProjectionDirty || this.isTransformDirty)) { + canSkip = false; + } + /** + * If we have resolved the target this frame we must recalculate the + * projection to ensure it visually represents the internal calculations. + */ + if (this.resolvedRelativeTargetAt === frameData.timestamp) { + canSkip = false; + } + if (canSkip) + return; + const { layout, layoutId } = this.options; + /** + * If this section of the tree isn't animating we can + * delete our target sources for the following frame. + */ + this.isTreeAnimating = Boolean((this.parent && this.parent.isTreeAnimating) || + this.currentAnimation || + this.pendingAnimation); + if (!this.isTreeAnimating) { + this.targetDelta = this.relativeTarget = undefined; + } + if (!this.layout || !(layout || layoutId)) + return; + /** + * Reset the corrected box with the latest values from box, as we're then going + * to perform mutative operations on it. + */ + copyBoxInto(this.layoutCorrected, this.layout.layoutBox); + /** + * Record previous tree scales before updating. + */ + const prevTreeScaleX = this.treeScale.x; + const prevTreeScaleY = this.treeScale.y; + /** + * Apply all the parent deltas to this box to produce the corrected box. This + * is the layout box, as it will appear on screen as a result of the transforms of its parents. + */ + applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared); + /** + * If this layer needs to perform scale correction but doesn't have a target, + * use the layout as the target. + */ + if (lead.layout && + !lead.target && + (this.treeScale.x !== 1 || this.treeScale.y !== 1)) { + lead.target = lead.layout.layoutBox; + lead.targetWithTransforms = createBox(); + } + const { target } = lead; + if (!target) { + /** + * If we don't have a target to project into, but we were previously + * projecting, we want to remove the stored transform and schedule + * a render to ensure the elements reflect the removed transform. + */ + if (this.prevProjectionDelta) { + this.createProjectionDeltas(); + this.scheduleRender(); + } + return; + } + if (!this.projectionDelta || !this.prevProjectionDelta) { + this.createProjectionDeltas(); + } + else { + copyAxisDeltaInto(this.prevProjectionDelta.x, this.projectionDelta.x); + copyAxisDeltaInto(this.prevProjectionDelta.y, this.projectionDelta.y); + } + /** + * Update the delta between the corrected box and the target box before user-set transforms were applied. + * This will allow us to calculate the corrected borderRadius and boxShadow to compensate + * for our layout reprojection, but still allow them to be scaled correctly by the user. + * It might be that to simplify this we may want to accept that user-set scale is also corrected + * and we wouldn't have to keep and calc both deltas, OR we could support a user setting + * to allow people to choose whether these styles are corrected based on just the + * layout reprojection or the final bounding box. + */ + calcBoxDelta(this.projectionDelta, this.layoutCorrected, target, this.latestValues); + if (this.treeScale.x !== prevTreeScaleX || + this.treeScale.y !== prevTreeScaleY || + !axisDeltaEquals(this.projectionDelta.x, this.prevProjectionDelta.x) || + !axisDeltaEquals(this.projectionDelta.y, this.prevProjectionDelta.y)) { + this.hasProjected = true; + this.scheduleRender(); + this.notifyListeners("projectionUpdate", target); + } + /** + * Increase debug counter for recalculated projections + */ + if (statsBuffer.value) { + metrics.calculatedProjections++; + } + } + hide() { + this.isVisible = false; + // TODO: Schedule render + } + show() { + this.isVisible = true; + // TODO: Schedule render + } + scheduleRender(notifyAll = true) { + this.options.visualElement?.scheduleRender(); + if (notifyAll) { + const stack = this.getStack(); + stack && stack.scheduleRender(); + } + if (this.resumingFrom && !this.resumingFrom.instance) { + this.resumingFrom = undefined; + } + } + createProjectionDeltas() { + this.prevProjectionDelta = createDelta(); + this.projectionDelta = createDelta(); + this.projectionDeltaWithTransform = createDelta(); + } + setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) { + const snapshot = this.snapshot; + const snapshotLatestValues = snapshot ? snapshot.latestValues : {}; + const mixedValues = { ...this.latestValues }; + const targetDelta = createDelta(); + if (!this.relativeParent || + !this.relativeParent.options.layoutRoot) { + this.relativeTarget = this.relativeTargetOrigin = undefined; + } + this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged; + const relativeLayout = createBox(); + const snapshotSource = snapshot ? snapshot.source : undefined; + const layoutSource = this.layout ? this.layout.source : undefined; + const isSharedLayoutAnimation = snapshotSource !== layoutSource; + const stack = this.getStack(); + const isOnlyMember = !stack || stack.members.length <= 1; + const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation && + !isOnlyMember && + this.options.crossfade === true && + !this.path.some(hasOpacityCrossfade)); + this.animationProgress = 0; + let prevRelativeTarget; + this.mixTargetDelta = (latest) => { + const progress = latest / 1000; + mixAxisDelta(targetDelta.x, delta.x, progress); + mixAxisDelta(targetDelta.y, delta.y, progress); + this.setTargetDelta(targetDelta); + if (this.relativeTarget && + this.relativeTargetOrigin && + this.layout && + this.relativeParent && + this.relativeParent.layout) { + calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox); + mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress); + /** + * If this is an unchanged relative target we can consider the + * projection not dirty. + */ + if (prevRelativeTarget && + boxEquals(this.relativeTarget, prevRelativeTarget)) { + this.isProjectionDirty = false; + } + if (!prevRelativeTarget) + prevRelativeTarget = createBox(); + copyBoxInto(prevRelativeTarget, this.relativeTarget); + } + if (isSharedLayoutAnimation) { + this.animationValues = mixedValues; + mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember); + } + this.root.scheduleUpdateProjection(); + this.scheduleRender(); + this.animationProgress = progress; + }; + this.mixTargetDelta(this.options.layoutRoot ? 1000 : 0); + } + startAnimation(options) { + this.notifyListeners("animationStart"); + this.currentAnimation?.stop(); + this.resumingFrom?.currentAnimation?.stop(); + if (this.pendingAnimation) { + cancelFrame(this.pendingAnimation); + this.pendingAnimation = undefined; + } + /** + * Start the animation in the next frame to have a frame with progress 0, + * where the target is the same as when the animation started, so we can + * calculate the relative positions correctly for instant transitions. + */ + this.pendingAnimation = frame.update(() => { + globalProjectionState.hasAnimatedSinceResize = true; + activeAnimations.layout++; + this.motionValue || (this.motionValue = motionValue(0)); + this.currentAnimation = animateSingleValue(this.motionValue, [0, 1000], { + ...options, + velocity: 0, + isSync: true, + onUpdate: (latest) => { + this.mixTargetDelta(latest); + options.onUpdate && options.onUpdate(latest); + }, + onStop: () => { + activeAnimations.layout--; + }, + onComplete: () => { + activeAnimations.layout--; + options.onComplete && options.onComplete(); + this.completeAnimation(); + }, + }); + if (this.resumingFrom) { + this.resumingFrom.currentAnimation = this.currentAnimation; + } + this.pendingAnimation = undefined; + }); + } + completeAnimation() { + if (this.resumingFrom) { + this.resumingFrom.currentAnimation = undefined; + this.resumingFrom.preserveOpacity = undefined; + } + const stack = this.getStack(); + stack && stack.exitAnimationComplete(); + this.resumingFrom = + this.currentAnimation = + this.animationValues = + undefined; + this.notifyListeners("animationComplete"); + } + finishAnimation() { + if (this.currentAnimation) { + this.mixTargetDelta && this.mixTargetDelta(animationTarget); + this.currentAnimation.stop(); + } + this.completeAnimation(); + } + applyTransformsToTarget() { + const lead = this.getLead(); + let { targetWithTransforms, target, layout, latestValues } = lead; + if (!targetWithTransforms || !target || !layout) + return; + /** + * If we're only animating position, and this element isn't the lead element, + * then instead of projecting into the lead box we instead want to calculate + * a new target that aligns the two boxes but maintains the layout shape. + */ + if (this !== lead && + this.layout && + layout && + shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) { + target = this.target || createBox(); + const xLength = calcLength(this.layout.layoutBox.x); + target.x.min = lead.target.x.min; + target.x.max = target.x.min + xLength; + const yLength = calcLength(this.layout.layoutBox.y); + target.y.min = lead.target.y.min; + target.y.max = target.y.min + yLength; + } + copyBoxInto(targetWithTransforms, target); + /** + * Apply the latest user-set transforms to the targetBox to produce the targetBoxFinal. + * This is the final box that we will then project into by calculating a transform delta and + * applying it to the corrected box. + */ + transformBox(targetWithTransforms, latestValues); + /** + * Update the delta between the corrected box and the final target box, after + * user-set transforms are applied to it. This will be used by the renderer to + * create a transform style that will reproject the element from its layout layout + * into the desired bounding box. + */ + calcBoxDelta(this.projectionDeltaWithTransform, this.layoutCorrected, targetWithTransforms, latestValues); + } + registerSharedNode(layoutId, node) { + if (!this.sharedNodes.has(layoutId)) { + this.sharedNodes.set(layoutId, new NodeStack()); + } + const stack = this.sharedNodes.get(layoutId); + stack.add(node); + const config = node.options.initialPromotionConfig; + node.promote({ + transition: config ? config.transition : undefined, + preserveFollowOpacity: config && config.shouldPreserveFollowOpacity + ? config.shouldPreserveFollowOpacity(node) + : undefined, + }); + } + isLead() { + const stack = this.getStack(); + return stack ? stack.lead === this : true; + } + getLead() { + const { layoutId } = this.options; + return layoutId ? this.getStack()?.lead || this : this; + } + getPrevLead() { + const { layoutId } = this.options; + return layoutId ? this.getStack()?.prevLead : undefined; + } + getStack() { + const { layoutId } = this.options; + if (layoutId) + return this.root.sharedNodes.get(layoutId); + } + promote({ needsReset, transition, preserveFollowOpacity, } = {}) { + const stack = this.getStack(); + if (stack) + stack.promote(this, preserveFollowOpacity); + if (needsReset) { + this.projectionDelta = undefined; + this.needsReset = true; + } + if (transition) + this.setOptions({ transition }); + } + relegate() { + const stack = this.getStack(); + if (stack) { + return stack.relegate(this); + } + else { + return false; + } + } + resetSkewAndRotation() { + const { visualElement } = this.options; + if (!visualElement) + return; + // If there's no detected skew or rotation values, we can early return without a forced render. + let hasDistortingTransform = false; + /** + * An unrolled check for rotation values. Most elements don't have any rotation and + * skipping the nested loop and new object creation is 50% faster. + */ + const { latestValues } = visualElement; + if (latestValues.z || + latestValues.rotate || + latestValues.rotateX || + latestValues.rotateY || + latestValues.rotateZ || + latestValues.skewX || + latestValues.skewY) { + hasDistortingTransform = true; + } + // If there's no distorting values, we don't need to do any more. + if (!hasDistortingTransform) + return; + const resetValues = {}; + if (latestValues.z) { + resetDistortingTransform("z", visualElement, resetValues, this.animationValues); + } + // Check the skew and rotate value of all axes and reset to 0 + for (let i = 0; i < transformAxes.length; i++) { + resetDistortingTransform(`rotate${transformAxes[i]}`, visualElement, resetValues, this.animationValues); + resetDistortingTransform(`skew${transformAxes[i]}`, visualElement, resetValues, this.animationValues); + } + // Force a render of this element to apply the transform with all skews and rotations + // set to 0. + visualElement.render(); + // Put back all the values we reset + for (const key in resetValues) { + visualElement.setStaticValue(key, resetValues[key]); + if (this.animationValues) { + this.animationValues[key] = resetValues[key]; + } + } + // Schedule a render for the next frame. This ensures we won't visually + // see the element with the reset rotate value applied. + visualElement.scheduleRender(); + } + applyProjectionStyles(targetStyle, // CSSStyleDeclaration - doesn't allow numbers to be assigned to properties + styleProp) { + if (!this.instance || this.isSVG) + return; + if (!this.isVisible) { + targetStyle.visibility = "hidden"; + return; + } + const transformTemplate = this.getTransformTemplate(); + if (this.needsReset) { + this.needsReset = false; + targetStyle.visibility = ""; + targetStyle.opacity = ""; + targetStyle.pointerEvents = + resolveMotionValue(styleProp?.pointerEvents) || ""; + targetStyle.transform = transformTemplate + ? transformTemplate(this.latestValues, "") + : "none"; + return; + } + const lead = this.getLead(); + if (!this.projectionDelta || !this.layout || !lead.target) { + if (this.options.layoutId) { + targetStyle.opacity = + this.latestValues.opacity !== undefined + ? this.latestValues.opacity + : 1; + targetStyle.pointerEvents = + resolveMotionValue(styleProp?.pointerEvents) || ""; + } + if (this.hasProjected && !hasTransform(this.latestValues)) { + targetStyle.transform = transformTemplate + ? transformTemplate({}, "") + : "none"; + this.hasProjected = false; + } + return; + } + targetStyle.visibility = ""; + const valuesToRender = lead.animationValues || lead.latestValues; + this.applyTransformsToTarget(); + let transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender); + if (transformTemplate) { + transform = transformTemplate(valuesToRender, transform); + } + targetStyle.transform = transform; + const { x, y } = this.projectionDelta; + targetStyle.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`; + if (lead.animationValues) { + /** + * If the lead component is animating, assign this either the entering/leaving + * opacity + */ + targetStyle.opacity = + lead === this + ? valuesToRender.opacity ?? + this.latestValues.opacity ?? + 1 + : this.preserveOpacity + ? this.latestValues.opacity + : valuesToRender.opacityExit; + } + else { + /** + * Or we're not animating at all, set the lead component to its layout + * opacity and other components to hidden. + */ + targetStyle.opacity = + lead === this + ? valuesToRender.opacity !== undefined + ? valuesToRender.opacity + : "" + : valuesToRender.opacityExit !== undefined + ? valuesToRender.opacityExit + : 0; + } + /** + * Apply scale correction + */ + for (const key in scaleCorrectors) { + if (valuesToRender[key] === undefined) + continue; + const { correct, applyTo, isCSSVariable } = scaleCorrectors[key]; + /** + * Only apply scale correction to the value if we have an + * active projection transform. Otherwise these values become + * vulnerable to distortion if the element changes size without + * a corresponding layout animation. + */ + const corrected = transform === "none" + ? valuesToRender[key] + : correct(valuesToRender[key], lead); + if (applyTo) { + const num = applyTo.length; + for (let i = 0; i < num; i++) { + targetStyle[applyTo[i]] = corrected; + } + } + else { + // If this is a CSS variable, set it directly on the instance. + // Replacing this function from creating styles to setting them + // would be a good place to remove per frame object creation + if (isCSSVariable) { + this.options.visualElement.renderState.vars[key] = corrected; + } + else { + targetStyle[key] = corrected; + } + } + } + /** + * Disable pointer events on follow components. This is to ensure + * that if a follow component covers a lead component it doesn't block + * pointer events on the lead. + */ + if (this.options.layoutId) { + targetStyle.pointerEvents = + lead === this + ? resolveMotionValue(styleProp?.pointerEvents) || "" + : "none"; + } + } + clearSnapshot() { + this.resumeFrom = this.snapshot = undefined; + } + // Only run on root + resetTree() { + this.root.nodes.forEach((node) => node.currentAnimation?.stop()); + this.root.nodes.forEach(clearMeasurements); + this.root.sharedNodes.clear(); + } + }; +} +function updateLayout(node) { + node.updateLayout(); +} +function notifyLayoutUpdate(node) { + const snapshot = node.resumeFrom?.snapshot || node.snapshot; + if (node.isLead() && + node.layout && + snapshot && + node.hasListeners("didUpdate")) { + const { layoutBox: layout, measuredBox: measuredLayout } = node.layout; + const { animationType } = node.options; + const isShared = snapshot.source !== node.layout.source; + // TODO Maybe we want to also resize the layout snapshot so we don't trigger + // animations for instance if layout="size" and an element has only changed position + if (animationType === "size") { + eachAxis((axis) => { + const axisSnapshot = isShared + ? snapshot.measuredBox[axis] + : snapshot.layoutBox[axis]; + const length = calcLength(axisSnapshot); + axisSnapshot.min = layout[axis].min; + axisSnapshot.max = axisSnapshot.min + length; + }); + } + else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) { + eachAxis((axis) => { + const axisSnapshot = isShared + ? snapshot.measuredBox[axis] + : snapshot.layoutBox[axis]; + const length = calcLength(layout[axis]); + axisSnapshot.max = axisSnapshot.min + length; + /** + * Ensure relative target gets resized and rerendererd + */ + if (node.relativeTarget && !node.currentAnimation) { + node.isProjectionDirty = true; + node.relativeTarget[axis].max = + node.relativeTarget[axis].min + length; + } + }); + } + const layoutDelta = createDelta(); + calcBoxDelta(layoutDelta, layout, snapshot.layoutBox); + const visualDelta = createDelta(); + if (isShared) { + calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox); + } + else { + calcBoxDelta(visualDelta, layout, snapshot.layoutBox); + } + const hasLayoutChanged = !isDeltaZero(layoutDelta); + let hasRelativeLayoutChanged = false; + if (!node.resumeFrom) { + const relativeParent = node.getClosestProjectingParent(); + /** + * If the relativeParent is itself resuming from a different element then + * the relative snapshot is not relavent + */ + if (relativeParent && !relativeParent.resumeFrom) { + const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent; + if (parentSnapshot && parentLayout) { + const relativeSnapshot = createBox(); + calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox); + const relativeLayout = createBox(); + calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox); + if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) { + hasRelativeLayoutChanged = true; + } + if (relativeParent.options.layoutRoot) { + node.relativeTarget = relativeLayout; + node.relativeTargetOrigin = relativeSnapshot; + node.relativeParent = relativeParent; + } + } + } + } + node.notifyListeners("didUpdate", { + layout, + snapshot, + delta: visualDelta, + layoutDelta, + hasLayoutChanged, + hasRelativeLayoutChanged, + }); + } + else if (node.isLead()) { + const { onExitComplete } = node.options; + onExitComplete && onExitComplete(); + } + /** + * Clearing transition + * TODO: Investigate why this transition is being passed in as {type: false } from Framer + * and why we need it at all + */ + node.options.transition = undefined; +} +function propagateDirtyNodes(node) { + /** + * Increase debug counter for nodes encountered this frame + */ + if (statsBuffer.value) { + metrics.nodes++; + } + if (!node.parent) + return; + /** + * If this node isn't projecting, propagate isProjectionDirty. It will have + * no performance impact but it will allow the next child that *is* projecting + * but *isn't* dirty to just check its parent to see if *any* ancestor needs + * correcting. + */ + if (!node.isProjecting()) { + node.isProjectionDirty = node.parent.isProjectionDirty; + } + /** + * Propagate isSharedProjectionDirty and isTransformDirty + * throughout the whole tree. A future revision can take another look at + * this but for safety we still recalcualte shared nodes. + */ + node.isSharedProjectionDirty || (node.isSharedProjectionDirty = Boolean(node.isProjectionDirty || + node.parent.isProjectionDirty || + node.parent.isSharedProjectionDirty)); + node.isTransformDirty || (node.isTransformDirty = node.parent.isTransformDirty); +} +function cleanDirtyNodes(node) { + node.isProjectionDirty = + node.isSharedProjectionDirty = + node.isTransformDirty = + false; +} +function clearSnapshot(node) { + node.clearSnapshot(); +} +function clearMeasurements(node) { + node.clearMeasurements(); +} +function clearIsLayoutDirty(node) { + node.isLayoutDirty = false; +} +function resetTransformStyle(node) { + const { visualElement } = node.options; + if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) { + visualElement.notify("BeforeLayoutMeasure"); + } + node.resetTransform(); +} +function finishAnimation(node) { + node.finishAnimation(); + node.targetDelta = node.relativeTarget = node.target = undefined; + node.isProjectionDirty = true; +} +function resolveTargetDelta(node) { + node.resolveTargetDelta(); +} +function calcProjection(node) { + node.calcProjection(); +} +function resetSkewAndRotation(node) { + node.resetSkewAndRotation(); +} +function removeLeadSnapshots(stack) { + stack.removeLeadSnapshot(); +} +function mixAxisDelta(output, delta, p) { + output.translate = mixNumber(delta.translate, 0, p); + output.scale = mixNumber(delta.scale, 1, p); + output.origin = delta.origin; + output.originPoint = delta.originPoint; +} +function mixAxis(output, from, to, p) { + output.min = mixNumber(from.min, to.min, p); + output.max = mixNumber(from.max, to.max, p); +} +function mixBox(output, from, to, p) { + mixAxis(output.x, from.x, to.x, p); + mixAxis(output.y, from.y, to.y, p); +} +function hasOpacityCrossfade(node) { + return (node.animationValues && node.animationValues.opacityExit !== undefined); +} +const defaultLayoutTransition = { + duration: 0.45, + ease: [0.4, 0, 0.1, 1], +}; +const userAgentContains = (string) => typeof navigator !== "undefined" && + navigator.userAgent && + navigator.userAgent.toLowerCase().includes(string); +/** + * Measured bounding boxes must be rounded in Safari and + * left untouched in Chrome, otherwise non-integer layouts within scaled-up elements + * can appear to jump. + */ +const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/") + ? Math.round + : noop; +function roundAxis(axis) { + // Round to the nearest .5 pixels to support subpixel layouts + axis.min = roundPoint(axis.min); + axis.max = roundPoint(axis.max); +} +function roundBox(box) { + roundAxis(box.x); + roundAxis(box.y); +} +function shouldAnimatePositionOnly(animationType, snapshot, layout) { + return (animationType === "position" || + (animationType === "preserve-aspect" && + !isNear(aspectRatio(snapshot), aspectRatio(layout), 0.2))); +} +function checkNodeWasScrollRoot(node) { + return node !== node.root && node.scroll?.wasRoot; +} + +export { cleanDirtyNodes, createProjectionNode, mixAxis, mixAxisDelta, mixBox, propagateDirtyNodes }; diff --git a/node_modules/framer-motion/dist/es/projection/node/group.mjs b/node_modules/framer-motion/dist/es/projection/node/group.mjs new file mode 100644 index 00000000..0dd1fd8f --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/node/group.mjs @@ -0,0 +1,24 @@ +const notify = (node) => !node.isLayoutDirty && node.willUpdate(false); +function nodeGroup() { + const nodes = new Set(); + const subscriptions = new WeakMap(); + const dirtyAll = () => nodes.forEach(notify); + return { + add: (node) => { + nodes.add(node); + subscriptions.set(node, node.addEventListener("willUpdate", dirtyAll)); + }, + remove: (node) => { + nodes.delete(node); + const unsubscribe = subscriptions.get(node); + if (unsubscribe) { + unsubscribe(); + subscriptions.delete(node); + } + dirtyAll(); + }, + dirty: dirtyAll, + }; +} + +export { nodeGroup }; diff --git a/node_modules/framer-motion/dist/es/projection/node/state.mjs b/node_modules/framer-motion/dist/es/projection/node/state.mjs new file mode 100644 index 00000000..e516e610 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/node/state.mjs @@ -0,0 +1,19 @@ +/** + * This should only ever be modified on the client otherwise it'll + * persist through server requests. If we need instanced states we + * could lazy-init via root. + */ +const globalProjectionState = { + /** + * Global flag as to whether the tree has animated since the last time + * we resized the window + */ + hasAnimatedSinceResize: true, + /** + * We set this to true once, on the first update. Any nodes added to the tree beyond that + * update will be given a `data-projection-id` attribute. + */ + hasEverUpdated: false, +}; + +export { globalProjectionState }; diff --git a/node_modules/framer-motion/dist/es/projection/shared/stack.mjs b/node_modules/framer-motion/dist/es/projection/shared/stack.mjs new file mode 100644 index 00000000..da4af5a8 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/shared/stack.mjs @@ -0,0 +1,112 @@ +import { addUniqueItem, removeItem } from 'motion-utils'; + +class NodeStack { + constructor() { + this.members = []; + } + add(node) { + addUniqueItem(this.members, node); + node.scheduleRender(); + } + remove(node) { + removeItem(this.members, node); + if (node === this.prevLead) { + this.prevLead = undefined; + } + if (node === this.lead) { + const prevLead = this.members[this.members.length - 1]; + if (prevLead) { + this.promote(prevLead); + } + } + } + relegate(node) { + const indexOfNode = this.members.findIndex((member) => node === member); + if (indexOfNode === 0) + return false; + /** + * Find the next projection node that is present + */ + let prevLead; + for (let i = indexOfNode; i >= 0; i--) { + const member = this.members[i]; + if (member.isPresent !== false) { + prevLead = member; + break; + } + } + if (prevLead) { + this.promote(prevLead); + return true; + } + else { + return false; + } + } + promote(node, preserveFollowOpacity) { + const prevLead = this.lead; + if (node === prevLead) + return; + this.prevLead = prevLead; + this.lead = node; + node.show(); + if (prevLead) { + prevLead.instance && prevLead.scheduleRender(); + node.scheduleRender(); + node.resumeFrom = prevLead; + if (preserveFollowOpacity) { + node.resumeFrom.preserveOpacity = true; + } + if (prevLead.snapshot) { + node.snapshot = prevLead.snapshot; + node.snapshot.latestValues = + prevLead.animationValues || prevLead.latestValues; + } + if (node.root && node.root.isUpdating) { + node.isLayoutDirty = true; + } + const { crossfade } = node.options; + if (crossfade === false) { + prevLead.hide(); + } + /** + * TODO: + * - Test border radius when previous node was deleted + * - boxShadow mixing + * - Shared between element A in scrolled container and element B (scroll stays the same or changes) + * - Shared between element A in transformed container and element B (transform stays the same or changes) + * - Shared between element A in scrolled page and element B (scroll stays the same or changes) + * --- + * - Crossfade opacity of root nodes + * - layoutId changes after animation + * - layoutId changes mid animation + */ + } + } + exitAnimationComplete() { + this.members.forEach((node) => { + const { options, resumingFrom } = node; + options.onExitComplete && options.onExitComplete(); + if (resumingFrom) { + resumingFrom.options.onExitComplete && + resumingFrom.options.onExitComplete(); + } + }); + } + scheduleRender() { + this.members.forEach((node) => { + node.instance && node.scheduleRender(false); + }); + } + /** + * Clear any leads that have been removed this render to prevent them from being + * used in future animations and to prevent memory leaks + */ + removeLeadSnapshot() { + if (this.lead && this.lead.snapshot) { + this.lead.snapshot = undefined; + } + } +} + +export { NodeStack }; diff --git a/node_modules/framer-motion/dist/es/projection/styles/scale-border-radius.mjs b/node_modules/framer-motion/dist/es/projection/styles/scale-border-radius.mjs new file mode 100644 index 00000000..b827baaf --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/styles/scale-border-radius.mjs @@ -0,0 +1,41 @@ +import { px } from 'motion-dom'; + +function pixelsToPercent(pixels, axis) { + if (axis.max === axis.min) + return 0; + return (pixels / (axis.max - axis.min)) * 100; +} +/** + * We always correct borderRadius as a percentage rather than pixels to reduce paints. + * For example, if you are projecting a box that is 100px wide with a 10px borderRadius + * into a box that is 200px wide with a 20px borderRadius, that is actually a 10% + * borderRadius in both states. If we animate between the two in pixels that will trigger + * a paint each time. If we animate between the two in percentage we'll avoid a paint. + */ +const correctBorderRadius = { + correct: (latest, node) => { + if (!node.target) + return latest; + /** + * If latest is a string, if it's a percentage we can return immediately as it's + * going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number. + */ + if (typeof latest === "string") { + if (px.test(latest)) { + latest = parseFloat(latest); + } + else { + return latest; + } + } + /** + * If latest is a number, it's a pixel value. We use the current viewportBox to calculate that + * pixel value as a percentage of each axis + */ + const x = pixelsToPercent(latest, node.target.x); + const y = pixelsToPercent(latest, node.target.y); + return `${x}% ${y}%`; + }, +}; + +export { correctBorderRadius, pixelsToPercent }; diff --git a/node_modules/framer-motion/dist/es/projection/styles/scale-box-shadow.mjs b/node_modules/framer-motion/dist/es/projection/styles/scale-box-shadow.mjs new file mode 100644 index 00000000..eddf7a52 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/styles/scale-box-shadow.mjs @@ -0,0 +1,34 @@ +import { complex, mixNumber } from 'motion-dom'; + +const correctBoxShadow = { + correct: (latest, { treeScale, projectionDelta }) => { + const original = latest; + const shadow = complex.parse(latest); + // TODO: Doesn't support multiple shadows + if (shadow.length > 5) + return original; + const template = complex.createTransformer(latest); + const offset = typeof shadow[0] !== "number" ? 1 : 0; + // Calculate the overall context scale + const xScale = projectionDelta.x.scale * treeScale.x; + const yScale = projectionDelta.y.scale * treeScale.y; + shadow[0 + offset] /= xScale; + shadow[1 + offset] /= yScale; + /** + * Ideally we'd correct x and y scales individually, but because blur and + * spread apply to both we have to take a scale average and apply that instead. + * We could potentially improve the outcome of this by incorporating the ratio between + * the two scales. + */ + const averageScale = mixNumber(xScale, yScale, 0.5); + // Blur + if (typeof shadow[2 + offset] === "number") + shadow[2 + offset] /= averageScale; + // Spread + if (typeof shadow[3 + offset] === "number") + shadow[3 + offset] /= averageScale; + return template(shadow); + }, +}; + +export { correctBoxShadow }; diff --git a/node_modules/framer-motion/dist/es/projection/styles/scale-correction.mjs b/node_modules/framer-motion/dist/es/projection/styles/scale-correction.mjs new file mode 100644 index 00000000..c3dff7f7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/styles/scale-correction.mjs @@ -0,0 +1,13 @@ +import { isCSSVariableName } from 'motion-dom'; + +const scaleCorrectors = {}; +function addScaleCorrector(correctors) { + for (const key in correctors) { + scaleCorrectors[key] = correctors[key]; + if (isCSSVariableName(key)) { + scaleCorrectors[key].isCSSVariable = true; + } + } +} + +export { addScaleCorrector, scaleCorrectors }; diff --git a/node_modules/framer-motion/dist/es/projection/styles/transform.mjs b/node_modules/framer-motion/dist/es/projection/styles/transform.mjs new file mode 100644 index 00000000..1f5b5615 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/styles/transform.mjs @@ -0,0 +1,49 @@ +function buildProjectionTransform(delta, treeScale, latestTransform) { + let transform = ""; + /** + * The translations we use to calculate are always relative to the viewport coordinate space. + * But when we apply scales, we also scale the coordinate space of an element and its children. + * For instance if we have a treeScale (the culmination of all parent scales) of 0.5 and we need + * to move an element 100 pixels, we actually need to move it 200 in within that scaled space. + */ + const xTranslate = delta.x.translate / treeScale.x; + const yTranslate = delta.y.translate / treeScale.y; + const zTranslate = latestTransform?.z || 0; + if (xTranslate || yTranslate || zTranslate) { + transform = `translate3d(${xTranslate}px, ${yTranslate}px, ${zTranslate}px) `; + } + /** + * Apply scale correction for the tree transform. + * This will apply scale to the screen-orientated axes. + */ + if (treeScale.x !== 1 || treeScale.y !== 1) { + transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `; + } + if (latestTransform) { + const { transformPerspective, rotate, rotateX, rotateY, skewX, skewY } = latestTransform; + if (transformPerspective) + transform = `perspective(${transformPerspective}px) ${transform}`; + if (rotate) + transform += `rotate(${rotate}deg) `; + if (rotateX) + transform += `rotateX(${rotateX}deg) `; + if (rotateY) + transform += `rotateY(${rotateY}deg) `; + if (skewX) + transform += `skewX(${skewX}deg) `; + if (skewY) + transform += `skewY(${skewY}deg) `; + } + /** + * Apply scale to match the size of the element to the size we want it. + * This will apply scale to the element-orientated axes. + */ + const elementScaleX = delta.x.scale * treeScale.x; + const elementScaleY = delta.y.scale * treeScale.y; + if (elementScaleX !== 1 || elementScaleY !== 1) { + transform += `scale(${elementScaleX}, ${elementScaleY})`; + } + return transform || "none"; +} + +export { buildProjectionTransform }; diff --git a/node_modules/framer-motion/dist/es/projection/use-instant-layout-transition.mjs b/node_modules/framer-motion/dist/es/projection/use-instant-layout-transition.mjs new file mode 100644 index 00000000..51a8297d --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/use-instant-layout-transition.mjs @@ -0,0 +1,14 @@ +import { rootProjectionNode } from './node/HTMLProjectionNode.mjs'; + +function useInstantLayoutTransition() { + return startTransition; +} +function startTransition(callback) { + if (!rootProjectionNode.current) + return; + rootProjectionNode.current.isUpdating = false; + rootProjectionNode.current.blockUpdate(); + callback && callback(); +} + +export { useInstantLayoutTransition }; diff --git a/node_modules/framer-motion/dist/es/projection/use-reset-projection.mjs b/node_modules/framer-motion/dist/es/projection/use-reset-projection.mjs new file mode 100644 index 00000000..5fa99326 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/use-reset-projection.mjs @@ -0,0 +1,14 @@ +import { useCallback } from 'react'; +import { rootProjectionNode } from './node/HTMLProjectionNode.mjs'; + +function useResetProjection() { + const reset = useCallback(() => { + const root = rootProjectionNode.current; + if (!root) + return; + root.resetTree(); + }, []); + return reset; +} + +export { useResetProjection }; diff --git a/node_modules/framer-motion/dist/es/projection/utils/each-axis.mjs b/node_modules/framer-motion/dist/es/projection/utils/each-axis.mjs new file mode 100644 index 00000000..e9434c72 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/utils/each-axis.mjs @@ -0,0 +1,5 @@ +function eachAxis(callback) { + return [callback("x"), callback("y")]; +} + +export { eachAxis }; diff --git a/node_modules/framer-motion/dist/es/projection/utils/has-transform.mjs b/node_modules/framer-motion/dist/es/projection/utils/has-transform.mjs new file mode 100644 index 00000000..d99d6e83 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/utils/has-transform.mjs @@ -0,0 +1,26 @@ +function isIdentityScale(scale) { + return scale === undefined || scale === 1; +} +function hasScale({ scale, scaleX, scaleY }) { + return (!isIdentityScale(scale) || + !isIdentityScale(scaleX) || + !isIdentityScale(scaleY)); +} +function hasTransform(values) { + return (hasScale(values) || + has2DTranslate(values) || + values.z || + values.rotate || + values.rotateX || + values.rotateY || + values.skewX || + values.skewY); +} +function has2DTranslate(values) { + return is2DTranslate(values.x) || is2DTranslate(values.y); +} +function is2DTranslate(value) { + return value && value !== "0%"; +} + +export { has2DTranslate, hasScale, hasTransform }; diff --git a/node_modules/framer-motion/dist/es/projection/utils/measure.mjs b/node_modules/framer-motion/dist/es/projection/utils/measure.mjs new file mode 100644 index 00000000..adcb3808 --- /dev/null +++ b/node_modules/framer-motion/dist/es/projection/utils/measure.mjs @@ -0,0 +1,17 @@ +import { convertBoundingBoxToBox, transformBoxPoints } from '../geometry/conversion.mjs'; +import { translateAxis } from '../geometry/delta-apply.mjs'; + +function measureViewportBox(instance, transformPoint) { + return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint)); +} +function measurePageBox(element, rootProjectionNode, transformPagePoint) { + const viewportBox = measureViewportBox(element, transformPagePoint); + const { scroll } = rootProjectionNode; + if (scroll) { + translateAxis(viewportBox.x, scroll.offset.x); + translateAxis(viewportBox.y, scroll.offset.y); + } + return viewportBox; +} + +export { measurePageBox, measureViewportBox }; diff --git a/node_modules/framer-motion/dist/es/render/VisualElement.mjs b/node_modules/framer-motion/dist/es/render/VisualElement.mjs new file mode 100644 index 00000000..b7210ddd --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/VisualElement.mjs @@ -0,0 +1,467 @@ +import { KeyframeResolver, time, frame, isMotionValue, cancelFrame, transformProps, motionValue, findValueType, complex, getAnimatableNone } from 'motion-dom'; +import { warnOnce, isNumericalString, isZeroValueString, SubscriptionManager } from 'motion-utils'; +import { featureDefinitions } from '../motion/features/definitions.mjs'; +import { createBox } from '../projection/geometry/models.mjs'; +import { initPrefersReducedMotion } from '../utils/reduced-motion/index.mjs'; +import { hasReducedMotionListener, prefersReducedMotion } from '../utils/reduced-motion/state.mjs'; +import { visualElementStore } from './store.mjs'; +import { isControllingVariants, isVariantNode } from './utils/is-controlling-variants.mjs'; +import { updateMotionValuesFromProps } from './utils/motion-values.mjs'; +import { resolveVariantFromProps } from './utils/resolve-variants.mjs'; + +const propEventHandlers = [ + "AnimationStart", + "AnimationComplete", + "Update", + "BeforeLayoutMeasure", + "LayoutMeasure", + "LayoutAnimationStart", + "LayoutAnimationComplete", +]; +/** + * A VisualElement is an imperative abstraction around UI elements such as + * HTMLElement, SVGElement, Three.Object3D etc. + */ +class VisualElement { + /** + * This method takes React props and returns found MotionValues. For example, HTML + * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays. + * + * This isn't an abstract method as it needs calling in the constructor, but it is + * intended to be one. + */ + scrapeMotionValuesFromProps(_props, _prevProps, _visualElement) { + return {}; + } + constructor({ parent, props, presenceContext, reducedMotionConfig, blockInitialAnimation, visualState, }, options = {}) { + /** + * A reference to the current underlying Instance, e.g. a HTMLElement + * or Three.Mesh etc. + */ + this.current = null; + /** + * A set containing references to this VisualElement's children. + */ + this.children = new Set(); + /** + * Determine what role this visual element should take in the variant tree. + */ + this.isVariantNode = false; + this.isControllingVariants = false; + /** + * Decides whether this VisualElement should animate in reduced motion + * mode. + * + * TODO: This is currently set on every individual VisualElement but feels + * like it could be set globally. + */ + this.shouldReduceMotion = null; + /** + * A map of all motion values attached to this visual element. Motion + * values are source of truth for any given animated value. A motion + * value might be provided externally by the component via props. + */ + this.values = new Map(); + this.KeyframeResolver = KeyframeResolver; + /** + * Cleanup functions for active features (hover/tap/exit etc) + */ + this.features = {}; + /** + * A map of every subscription that binds the provided or generated + * motion values onChange listeners to this visual element. + */ + this.valueSubscriptions = new Map(); + /** + * A reference to the previously-provided motion values as returned + * from scrapeMotionValuesFromProps. We use the keys in here to determine + * if any motion values need to be removed after props are updated. + */ + this.prevMotionValues = {}; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + /** + * An object containing an unsubscribe function for each prop event subscription. + * For example, every "Update" event can have multiple subscribers via + * VisualElement.on(), but only one of those can be defined via the onUpdate prop. + */ + this.propEventSubscriptions = {}; + this.notifyUpdate = () => this.notify("Update", this.latestValues); + this.render = () => { + if (!this.current) + return; + this.triggerBuild(); + this.renderInstance(this.current, this.renderState, this.props.style, this.projection); + }; + this.renderScheduledAt = 0.0; + this.scheduleRender = () => { + const now = time.now(); + if (this.renderScheduledAt < now) { + this.renderScheduledAt = now; + frame.render(this.render, false, true); + } + }; + const { latestValues, renderState } = visualState; + this.latestValues = latestValues; + this.baseTarget = { ...latestValues }; + this.initialValues = props.initial ? { ...latestValues } : {}; + this.renderState = renderState; + this.parent = parent; + this.props = props; + this.presenceContext = presenceContext; + this.depth = parent ? parent.depth + 1 : 0; + this.reducedMotionConfig = reducedMotionConfig; + this.options = options; + this.blockInitialAnimation = Boolean(blockInitialAnimation); + this.isControllingVariants = isControllingVariants(props); + this.isVariantNode = isVariantNode(props); + if (this.isVariantNode) { + this.variantChildren = new Set(); + } + this.manuallyAnimateOnMount = Boolean(parent && parent.current); + /** + * Any motion values that are provided to the element when created + * aren't yet bound to the element, as this would technically be impure. + * However, we iterate through the motion values and set them to the + * initial values for this component. + * + * TODO: This is impure and we should look at changing this to run on mount. + * Doing so will break some tests but this isn't necessarily a breaking change, + * more a reflection of the test. + */ + const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {}, this); + for (const key in initialMotionValues) { + const value = initialMotionValues[key]; + if (latestValues[key] !== undefined && isMotionValue(value)) { + value.set(latestValues[key], false); + } + } + } + mount(instance) { + this.current = instance; + visualElementStore.set(instance, this); + if (this.projection && !this.projection.instance) { + this.projection.mount(instance); + } + if (this.parent && this.isVariantNode && !this.isControllingVariants) { + this.removeFromVariantTree = this.parent.addVariantChild(this); + } + this.values.forEach((value, key) => this.bindToMotionValue(key, value)); + if (!hasReducedMotionListener.current) { + initPrefersReducedMotion(); + } + this.shouldReduceMotion = + this.reducedMotionConfig === "never" + ? false + : this.reducedMotionConfig === "always" + ? true + : prefersReducedMotion.current; + if (process.env.NODE_ENV !== "production") { + warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + if (this.parent) + this.parent.children.add(this); + this.update(this.props, this.presenceContext); + } + unmount() { + this.projection && this.projection.unmount(); + cancelFrame(this.notifyUpdate); + cancelFrame(this.render); + this.valueSubscriptions.forEach((remove) => remove()); + this.valueSubscriptions.clear(); + this.removeFromVariantTree && this.removeFromVariantTree(); + this.parent && this.parent.children.delete(this); + for (const key in this.events) { + this.events[key].clear(); + } + for (const key in this.features) { + const feature = this.features[key]; + if (feature) { + feature.unmount(); + feature.isMounted = false; + } + } + this.current = null; + } + bindToMotionValue(key, value) { + if (this.valueSubscriptions.has(key)) { + this.valueSubscriptions.get(key)(); + } + const valueIsTransform = transformProps.has(key); + if (valueIsTransform && this.onBindTransform) { + this.onBindTransform(); + } + const removeOnChange = value.on("change", (latestValue) => { + this.latestValues[key] = latestValue; + this.props.onUpdate && frame.preRender(this.notifyUpdate); + if (valueIsTransform && this.projection) { + this.projection.isTransformDirty = true; + } + }); + const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender); + let removeSyncCheck; + if (window.MotionCheckAppearSync) { + removeSyncCheck = window.MotionCheckAppearSync(this, key, value); + } + this.valueSubscriptions.set(key, () => { + removeOnChange(); + removeOnRenderRequest(); + if (removeSyncCheck) + removeSyncCheck(); + if (value.owner) + value.stop(); + }); + } + sortNodePosition(other) { + /** + * If these nodes aren't even of the same type we can't compare their depth. + */ + if (!this.current || + !this.sortInstanceNodePosition || + this.type !== other.type) { + return 0; + } + return this.sortInstanceNodePosition(this.current, other.current); + } + updateFeatures() { + let key = "animation"; + for (key in featureDefinitions) { + const featureDefinition = featureDefinitions[key]; + if (!featureDefinition) + continue; + const { isEnabled, Feature: FeatureConstructor } = featureDefinition; + /** + * If this feature is enabled but not active, make a new instance. + */ + if (!this.features[key] && + FeatureConstructor && + isEnabled(this.props)) { + this.features[key] = new FeatureConstructor(this); + } + /** + * If we have a feature, mount or update it. + */ + if (this.features[key]) { + const feature = this.features[key]; + if (feature.isMounted) { + feature.update(); + } + else { + feature.mount(); + feature.isMounted = true; + } + } + } + } + triggerBuild() { + this.build(this.renderState, this.latestValues, this.props); + } + /** + * Measure the current viewport box with or without transforms. + * Only measures axis-aligned boxes, rotate and skew must be manually + * removed with a re-render to work. + */ + measureViewportBox() { + return this.current + ? this.measureInstanceViewportBox(this.current, this.props) + : createBox(); + } + getStaticValue(key) { + return this.latestValues[key]; + } + setStaticValue(key, value) { + this.latestValues[key] = value; + } + /** + * Update the provided props. Ensure any newly-added motion values are + * added to our map, old ones removed, and listeners updated. + */ + update(props, presenceContext) { + if (props.transformTemplate || this.props.transformTemplate) { + this.scheduleRender(); + } + this.prevProps = this.props; + this.props = props; + this.prevPresenceContext = this.presenceContext; + this.presenceContext = presenceContext; + /** + * Update prop event handlers ie onAnimationStart, onAnimationComplete + */ + for (let i = 0; i < propEventHandlers.length; i++) { + const key = propEventHandlers[i]; + if (this.propEventSubscriptions[key]) { + this.propEventSubscriptions[key](); + delete this.propEventSubscriptions[key]; + } + const listenerName = ("on" + key); + const listener = props[listenerName]; + if (listener) { + this.propEventSubscriptions[key] = this.on(key, listener); + } + } + this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps, this), this.prevMotionValues); + if (this.handleChildMotionValue) { + this.handleChildMotionValue(); + } + } + getProps() { + return this.props; + } + /** + * Returns the variant definition with a given name. + */ + getVariant(name) { + return this.props.variants ? this.props.variants[name] : undefined; + } + /** + * Returns the defined default transition on this component. + */ + getDefaultTransition() { + return this.props.transition; + } + getTransformPagePoint() { + return this.props.transformPagePoint; + } + getClosestVariantNode() { + return this.isVariantNode + ? this + : this.parent + ? this.parent.getClosestVariantNode() + : undefined; + } + /** + * Add a child visual element to our set of children. + */ + addVariantChild(child) { + const closestVariantNode = this.getClosestVariantNode(); + if (closestVariantNode) { + closestVariantNode.variantChildren && + closestVariantNode.variantChildren.add(child); + return () => closestVariantNode.variantChildren.delete(child); + } + } + /** + * Add a motion value and bind it to this visual element. + */ + addValue(key, value) { + // Remove existing value if it exists + const existingValue = this.values.get(key); + if (value !== existingValue) { + if (existingValue) + this.removeValue(key); + this.bindToMotionValue(key, value); + this.values.set(key, value); + this.latestValues[key] = value.get(); + } + } + /** + * Remove a motion value and unbind any active subscriptions. + */ + removeValue(key) { + this.values.delete(key); + const unsubscribe = this.valueSubscriptions.get(key); + if (unsubscribe) { + unsubscribe(); + this.valueSubscriptions.delete(key); + } + delete this.latestValues[key]; + this.removeValueFromRenderState(key, this.renderState); + } + /** + * Check whether we have a motion value for this key + */ + hasValue(key) { + return this.values.has(key); + } + getValue(key, defaultValue) { + if (this.props.values && this.props.values[key]) { + return this.props.values[key]; + } + let value = this.values.get(key); + if (value === undefined && defaultValue !== undefined) { + value = motionValue(defaultValue === null ? undefined : defaultValue, { owner: this }); + this.addValue(key, value); + } + return value; + } + /** + * If we're trying to animate to a previously unencountered value, + * we need to check for it in our state and as a last resort read it + * directly from the instance (which might have performance implications). + */ + readValue(key, target) { + let value = this.latestValues[key] !== undefined || !this.current + ? this.latestValues[key] + : this.getBaseTargetFromProps(this.props, key) ?? + this.readValueFromInstance(this.current, key, this.options); + if (value !== undefined && value !== null) { + if (typeof value === "string" && + (isNumericalString(value) || isZeroValueString(value))) { + // If this is a number read as a string, ie "0" or "200", convert it to a number + value = parseFloat(value); + } + else if (!findValueType(value) && complex.test(target)) { + value = getAnimatableNone(key, target); + } + this.setBaseTarget(key, isMotionValue(value) ? value.get() : value); + } + return isMotionValue(value) ? value.get() : value; + } + /** + * Set the base target to later animate back to. This is currently + * only hydrated on creation and when we first read a value. + */ + setBaseTarget(key, value) { + this.baseTarget[key] = value; + } + /** + * Find the base target for a value thats been removed from all animation + * props. + */ + getBaseTarget(key) { + const { initial } = this.props; + let valueFromInitial; + if (typeof initial === "string" || typeof initial === "object") { + const variant = resolveVariantFromProps(this.props, initial, this.presenceContext?.custom); + if (variant) { + valueFromInitial = variant[key]; + } + } + /** + * If this value still exists in the current initial variant, read that. + */ + if (initial && valueFromInitial !== undefined) { + return valueFromInitial; + } + /** + * Alternatively, if this VisualElement config has defined a getBaseTarget + * so we can read the value from an alternative source, try that. + */ + const target = this.getBaseTargetFromProps(this.props, key); + if (target !== undefined && !isMotionValue(target)) + return target; + /** + * If the value was initially defined on initial, but it doesn't any more, + * return undefined. Otherwise return the value as initially read from the DOM. + */ + return this.initialValues[key] !== undefined && + valueFromInitial === undefined + ? undefined + : this.baseTarget[key]; + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new SubscriptionManager(); + } + return this.events[eventName].add(callback); + } + notify(eventName, ...args) { + if (this.events[eventName]) { + this.events[eventName].notify(...args); + } + } +} + +export { VisualElement }; diff --git a/node_modules/framer-motion/dist/es/render/components/create-factory.mjs b/node_modules/framer-motion/dist/es/render/components/create-factory.mjs new file mode 100644 index 00000000..c6a7d6dc --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/create-factory.mjs @@ -0,0 +1,23 @@ +import { createRendererMotionComponent } from '../../motion/index.mjs'; +import { createUseRender } from '../dom/use-render.mjs'; +import { isSVGComponent } from '../dom/utils/is-svg-component.mjs'; +import { htmlMotionConfig } from '../html/config-motion.mjs'; +import { svgMotionConfig } from '../svg/config-motion.mjs'; + +function createMotionComponentFactory(preloadedFeatures, createVisualElement) { + return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) { + const baseConfig = isSVGComponent(Component) + ? svgMotionConfig + : htmlMotionConfig; + const config = { + ...baseConfig, + preloadedFeatures, + useRender: createUseRender(forwardMotionProps), + createVisualElement, + Component, + }; + return createRendererMotionComponent(config); + }; +} + +export { createMotionComponentFactory }; diff --git a/node_modules/framer-motion/dist/es/render/components/create-proxy.mjs b/node_modules/framer-motion/dist/es/render/components/create-proxy.mjs new file mode 100644 index 00000000..f7cdd59b --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/create-proxy.mjs @@ -0,0 +1,38 @@ +import { warnOnce } from 'motion-utils'; + +function createDOMMotionComponentProxy(componentFactory) { + if (typeof Proxy === "undefined") { + return componentFactory; + } + /** + * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc. + * Rather than generating them anew every render. + */ + const componentCache = new Map(); + const deprecatedFactoryFunction = (...args) => { + if (process.env.NODE_ENV !== "production") { + warnOnce(false, "motion() is deprecated. Use motion.create() instead."); + } + return componentFactory(...args); + }; + return new Proxy(deprecatedFactoryFunction, { + /** + * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc. + * The prop name is passed through as `key` and we can use that to generate a `motion` + * DOM component with that name. + */ + get: (_target, key) => { + if (key === "create") + return componentFactory; + /** + * If this element doesn't exist in the component cache, create it and cache. + */ + if (!componentCache.has(key)) { + componentCache.set(key, componentFactory(key)); + } + return componentCache.get(key); + }, + }); +} + +export { createDOMMotionComponentProxy }; diff --git a/node_modules/framer-motion/dist/es/render/components/m/create.mjs b/node_modules/framer-motion/dist/es/render/components/m/create.mjs new file mode 100644 index 00000000..1181f06b --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/m/create.mjs @@ -0,0 +1,6 @@ +import { createMotionComponentFactory } from '../create-factory.mjs'; + +const createMinimalMotionComponent = +/*@__PURE__*/ createMotionComponentFactory(); + +export { createMinimalMotionComponent }; diff --git a/node_modules/framer-motion/dist/es/render/components/m/elements.mjs b/node_modules/framer-motion/dist/es/render/components/m/elements.mjs new file mode 100644 index 00000000..ac516d33 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/m/elements.mjs @@ -0,0 +1,227 @@ +"use client"; +import { createMinimalMotionComponent } from './create.mjs'; + +/** + * HTML components + */ +const MotionA = /*@__PURE__*/ createMinimalMotionComponent("a"); +const MotionAbbr = /*@__PURE__*/ createMinimalMotionComponent("abbr"); +const MotionAddress = +/*@__PURE__*/ createMinimalMotionComponent("address"); +const MotionArea = /*@__PURE__*/ createMinimalMotionComponent("area"); +const MotionArticle = +/*@__PURE__*/ createMinimalMotionComponent("article"); +const MotionAside = /*@__PURE__*/ createMinimalMotionComponent("aside"); +const MotionAudio = /*@__PURE__*/ createMinimalMotionComponent("audio"); +const MotionB = /*@__PURE__*/ createMinimalMotionComponent("b"); +const MotionBase = /*@__PURE__*/ createMinimalMotionComponent("base"); +const MotionBdi = /*@__PURE__*/ createMinimalMotionComponent("bdi"); +const MotionBdo = /*@__PURE__*/ createMinimalMotionComponent("bdo"); +const MotionBig = /*@__PURE__*/ createMinimalMotionComponent("big"); +const MotionBlockquote = +/*@__PURE__*/ createMinimalMotionComponent("blockquote"); +const MotionBody = /*@__PURE__*/ createMinimalMotionComponent("body"); +const MotionButton = /*@__PURE__*/ createMinimalMotionComponent("button"); +const MotionCanvas = /*@__PURE__*/ createMinimalMotionComponent("canvas"); +const MotionCaption = +/*@__PURE__*/ createMinimalMotionComponent("caption"); +const MotionCite = /*@__PURE__*/ createMinimalMotionComponent("cite"); +const MotionCode = /*@__PURE__*/ createMinimalMotionComponent("code"); +const MotionCol = /*@__PURE__*/ createMinimalMotionComponent("col"); +const MotionColgroup = +/*@__PURE__*/ createMinimalMotionComponent("colgroup"); +const MotionData = /*@__PURE__*/ createMinimalMotionComponent("data"); +const MotionDatalist = +/*@__PURE__*/ createMinimalMotionComponent("datalist"); +const MotionDd = /*@__PURE__*/ createMinimalMotionComponent("dd"); +const MotionDel = /*@__PURE__*/ createMinimalMotionComponent("del"); +const MotionDetails = +/*@__PURE__*/ createMinimalMotionComponent("details"); +const MotionDfn = /*@__PURE__*/ createMinimalMotionComponent("dfn"); +const MotionDialog = /*@__PURE__*/ createMinimalMotionComponent("dialog"); +const MotionDiv = /*@__PURE__*/ createMinimalMotionComponent("div"); +const MotionDl = /*@__PURE__*/ createMinimalMotionComponent("dl"); +const MotionDt = /*@__PURE__*/ createMinimalMotionComponent("dt"); +const MotionEm = /*@__PURE__*/ createMinimalMotionComponent("em"); +const MotionEmbed = /*@__PURE__*/ createMinimalMotionComponent("embed"); +const MotionFieldset = +/*@__PURE__*/ createMinimalMotionComponent("fieldset"); +const MotionFigcaption = +/*@__PURE__*/ createMinimalMotionComponent("figcaption"); +const MotionFigure = /*@__PURE__*/ createMinimalMotionComponent("figure"); +const MotionFooter = /*@__PURE__*/ createMinimalMotionComponent("footer"); +const MotionForm = /*@__PURE__*/ createMinimalMotionComponent("form"); +const MotionH1 = /*@__PURE__*/ createMinimalMotionComponent("h1"); +const MotionH2 = /*@__PURE__*/ createMinimalMotionComponent("h2"); +const MotionH3 = /*@__PURE__*/ createMinimalMotionComponent("h3"); +const MotionH4 = /*@__PURE__*/ createMinimalMotionComponent("h4"); +const MotionH5 = /*@__PURE__*/ createMinimalMotionComponent("h5"); +const MotionH6 = /*@__PURE__*/ createMinimalMotionComponent("h6"); +const MotionHead = /*@__PURE__*/ createMinimalMotionComponent("head"); +const MotionHeader = /*@__PURE__*/ createMinimalMotionComponent("header"); +const MotionHgroup = /*@__PURE__*/ createMinimalMotionComponent("hgroup"); +const MotionHr = /*@__PURE__*/ createMinimalMotionComponent("hr"); +const MotionHtml = /*@__PURE__*/ createMinimalMotionComponent("html"); +const MotionI = /*@__PURE__*/ createMinimalMotionComponent("i"); +const MotionIframe = /*@__PURE__*/ createMinimalMotionComponent("iframe"); +const MotionImg = /*@__PURE__*/ createMinimalMotionComponent("img"); +const MotionInput = /*@__PURE__*/ createMinimalMotionComponent("input"); +const MotionIns = /*@__PURE__*/ createMinimalMotionComponent("ins"); +const MotionKbd = /*@__PURE__*/ createMinimalMotionComponent("kbd"); +const MotionKeygen = /*@__PURE__*/ createMinimalMotionComponent("keygen"); +const MotionLabel = /*@__PURE__*/ createMinimalMotionComponent("label"); +const MotionLegend = /*@__PURE__*/ createMinimalMotionComponent("legend"); +const MotionLi = /*@__PURE__*/ createMinimalMotionComponent("li"); +const MotionLink = /*@__PURE__*/ createMinimalMotionComponent("link"); +const MotionMain = /*@__PURE__*/ createMinimalMotionComponent("main"); +const MotionMap = /*@__PURE__*/ createMinimalMotionComponent("map"); +const MotionMark = /*@__PURE__*/ createMinimalMotionComponent("mark"); +const MotionMenu = /*@__PURE__*/ createMinimalMotionComponent("menu"); +const MotionMenuitem = +/*@__PURE__*/ createMinimalMotionComponent("menuitem"); +const MotionMeter = /*@__PURE__*/ createMinimalMotionComponent("meter"); +const MotionNav = /*@__PURE__*/ createMinimalMotionComponent("nav"); +const MotionObject = /*@__PURE__*/ createMinimalMotionComponent("object"); +const MotionOl = /*@__PURE__*/ createMinimalMotionComponent("ol"); +const MotionOptgroup = +/*@__PURE__*/ createMinimalMotionComponent("optgroup"); +const MotionOption = /*@__PURE__*/ createMinimalMotionComponent("option"); +const MotionOutput = /*@__PURE__*/ createMinimalMotionComponent("output"); +const MotionP = /*@__PURE__*/ createMinimalMotionComponent("p"); +const MotionParam = /*@__PURE__*/ createMinimalMotionComponent("param"); +const MotionPicture = +/*@__PURE__*/ createMinimalMotionComponent("picture"); +const MotionPre = /*@__PURE__*/ createMinimalMotionComponent("pre"); +const MotionProgress = +/*@__PURE__*/ createMinimalMotionComponent("progress"); +const MotionQ = /*@__PURE__*/ createMinimalMotionComponent("q"); +const MotionRp = /*@__PURE__*/ createMinimalMotionComponent("rp"); +const MotionRt = /*@__PURE__*/ createMinimalMotionComponent("rt"); +const MotionRuby = /*@__PURE__*/ createMinimalMotionComponent("ruby"); +const MotionS = /*@__PURE__*/ createMinimalMotionComponent("s"); +const MotionSamp = /*@__PURE__*/ createMinimalMotionComponent("samp"); +const MotionScript = /*@__PURE__*/ createMinimalMotionComponent("script"); +const MotionSection = +/*@__PURE__*/ createMinimalMotionComponent("section"); +const MotionSelect = /*@__PURE__*/ createMinimalMotionComponent("select"); +const MotionSmall = /*@__PURE__*/ createMinimalMotionComponent("small"); +const MotionSource = /*@__PURE__*/ createMinimalMotionComponent("source"); +const MotionSpan = /*@__PURE__*/ createMinimalMotionComponent("span"); +const MotionStrong = /*@__PURE__*/ createMinimalMotionComponent("strong"); +const MotionStyle = /*@__PURE__*/ createMinimalMotionComponent("style"); +const MotionSub = /*@__PURE__*/ createMinimalMotionComponent("sub"); +const MotionSummary = +/*@__PURE__*/ createMinimalMotionComponent("summary"); +const MotionSup = /*@__PURE__*/ createMinimalMotionComponent("sup"); +const MotionTable = /*@__PURE__*/ createMinimalMotionComponent("table"); +const MotionTbody = /*@__PURE__*/ createMinimalMotionComponent("tbody"); +const MotionTd = /*@__PURE__*/ createMinimalMotionComponent("td"); +const MotionTextarea = +/*@__PURE__*/ createMinimalMotionComponent("textarea"); +const MotionTfoot = /*@__PURE__*/ createMinimalMotionComponent("tfoot"); +const MotionTh = /*@__PURE__*/ createMinimalMotionComponent("th"); +const MotionThead = /*@__PURE__*/ createMinimalMotionComponent("thead"); +const MotionTime = /*@__PURE__*/ createMinimalMotionComponent("time"); +const MotionTitle = /*@__PURE__*/ createMinimalMotionComponent("title"); +const MotionTr = /*@__PURE__*/ createMinimalMotionComponent("tr"); +const MotionTrack = /*@__PURE__*/ createMinimalMotionComponent("track"); +const MotionU = /*@__PURE__*/ createMinimalMotionComponent("u"); +const MotionUl = /*@__PURE__*/ createMinimalMotionComponent("ul"); +const MotionVideo = /*@__PURE__*/ createMinimalMotionComponent("video"); +const MotionWbr = /*@__PURE__*/ createMinimalMotionComponent("wbr"); +const MotionWebview = +/*@__PURE__*/ createMinimalMotionComponent("webview"); +/** + * SVG components + */ +const MotionAnimate = +/*@__PURE__*/ createMinimalMotionComponent("animate"); +const MotionCircle = /*@__PURE__*/ createMinimalMotionComponent("circle"); +const MotionDefs = /*@__PURE__*/ createMinimalMotionComponent("defs"); +const MotionDesc = /*@__PURE__*/ createMinimalMotionComponent("desc"); +const MotionEllipse = +/*@__PURE__*/ createMinimalMotionComponent("ellipse"); +const MotionG = /*@__PURE__*/ createMinimalMotionComponent("g"); +const MotionImage = /*@__PURE__*/ createMinimalMotionComponent("image"); +const MotionLine = /*@__PURE__*/ createMinimalMotionComponent("line"); +const MotionFilter = /*@__PURE__*/ createMinimalMotionComponent("filter"); +const MotionMarker = /*@__PURE__*/ createMinimalMotionComponent("marker"); +const MotionMask = /*@__PURE__*/ createMinimalMotionComponent("mask"); +const MotionMetadata = +/*@__PURE__*/ createMinimalMotionComponent("metadata"); +const MotionPath = /*@__PURE__*/ createMinimalMotionComponent("path"); +const MotionPattern = +/*@__PURE__*/ createMinimalMotionComponent("pattern"); +const MotionPolygon = +/*@__PURE__*/ createMinimalMotionComponent("polygon"); +const MotionPolyline = +/*@__PURE__*/ createMinimalMotionComponent("polyline"); +const MotionRect = /*@__PURE__*/ createMinimalMotionComponent("rect"); +const MotionStop = /*@__PURE__*/ createMinimalMotionComponent("stop"); +const MotionSvg = /*@__PURE__*/ createMinimalMotionComponent("svg"); +const MotionSymbol = /*@__PURE__*/ createMinimalMotionComponent("symbol"); +const MotionText = /*@__PURE__*/ createMinimalMotionComponent("text"); +const MotionTspan = /*@__PURE__*/ createMinimalMotionComponent("tspan"); +const MotionUse = /*@__PURE__*/ createMinimalMotionComponent("use"); +const MotionView = /*@__PURE__*/ createMinimalMotionComponent("view"); +const MotionClipPath = +/*@__PURE__*/ createMinimalMotionComponent("clipPath"); +const MotionFeBlend = +/*@__PURE__*/ createMinimalMotionComponent("feBlend"); +const MotionFeColorMatrix = +/*@__PURE__*/ createMinimalMotionComponent("feColorMatrix"); +const MotionFeComponentTransfer = +/*@__PURE__*/ createMinimalMotionComponent("feComponentTransfer"); +const MotionFeComposite = +/*@__PURE__*/ createMinimalMotionComponent("feComposite"); +const MotionFeConvolveMatrix = +/*@__PURE__*/ createMinimalMotionComponent("feConvolveMatrix"); +const MotionFeDiffuseLighting = +/*@__PURE__*/ createMinimalMotionComponent("feDiffuseLighting"); +const MotionFeDisplacementMap = +/*@__PURE__*/ createMinimalMotionComponent("feDisplacementMap"); +const MotionFeDistantLight = +/*@__PURE__*/ createMinimalMotionComponent("feDistantLight"); +const MotionFeDropShadow = +/*@__PURE__*/ createMinimalMotionComponent("feDropShadow"); +const MotionFeFlood = +/*@__PURE__*/ createMinimalMotionComponent("feFlood"); +const MotionFeFuncA = +/*@__PURE__*/ createMinimalMotionComponent("feFuncA"); +const MotionFeFuncB = +/*@__PURE__*/ createMinimalMotionComponent("feFuncB"); +const MotionFeFuncG = +/*@__PURE__*/ createMinimalMotionComponent("feFuncG"); +const MotionFeFuncR = +/*@__PURE__*/ createMinimalMotionComponent("feFuncR"); +const MotionFeGaussianBlur = +/*@__PURE__*/ createMinimalMotionComponent("feGaussianBlur"); +const MotionFeImage = +/*@__PURE__*/ createMinimalMotionComponent("feImage"); +const MotionFeMerge = +/*@__PURE__*/ createMinimalMotionComponent("feMerge"); +const MotionFeMergeNode = +/*@__PURE__*/ createMinimalMotionComponent("feMergeNode"); +const MotionFeMorphology = +/*@__PURE__*/ createMinimalMotionComponent("feMorphology"); +const MotionFeOffset = +/*@__PURE__*/ createMinimalMotionComponent("feOffset"); +const MotionFePointLight = +/*@__PURE__*/ createMinimalMotionComponent("fePointLight"); +const MotionFeSpecularLighting = +/*@__PURE__*/ createMinimalMotionComponent("feSpecularLighting"); +const MotionFeSpotLight = +/*@__PURE__*/ createMinimalMotionComponent("feSpotLight"); +const MotionFeTile = /*@__PURE__*/ createMinimalMotionComponent("feTile"); +const MotionFeTurbulence = +/*@__PURE__*/ createMinimalMotionComponent("feTurbulence"); +const MotionForeignObject = +/*@__PURE__*/ createMinimalMotionComponent("foreignObject"); +const MotionLinearGradient = +/*@__PURE__*/ createMinimalMotionComponent("linearGradient"); +const MotionRadialGradient = +/*@__PURE__*/ createMinimalMotionComponent("radialGradient"); +const MotionTextPath = +/*@__PURE__*/ createMinimalMotionComponent("textPath"); + +export { MotionA, MotionAbbr, MotionAddress, MotionAnimate, MotionArea, MotionArticle, MotionAside, MotionAudio, MotionB, MotionBase, MotionBdi, MotionBdo, MotionBig, MotionBlockquote, MotionBody, MotionButton, MotionCanvas, MotionCaption, MotionCircle, MotionCite, MotionClipPath, MotionCode, MotionCol, MotionColgroup, MotionData, MotionDatalist, MotionDd, MotionDefs, MotionDel, MotionDesc, MotionDetails, MotionDfn, MotionDialog, MotionDiv, MotionDl, MotionDt, MotionEllipse, MotionEm, MotionEmbed, MotionFeBlend, MotionFeColorMatrix, MotionFeComponentTransfer, MotionFeComposite, MotionFeConvolveMatrix, MotionFeDiffuseLighting, MotionFeDisplacementMap, MotionFeDistantLight, MotionFeDropShadow, MotionFeFlood, MotionFeFuncA, MotionFeFuncB, MotionFeFuncG, MotionFeFuncR, MotionFeGaussianBlur, MotionFeImage, MotionFeMerge, MotionFeMergeNode, MotionFeMorphology, MotionFeOffset, MotionFePointLight, MotionFeSpecularLighting, MotionFeSpotLight, MotionFeTile, MotionFeTurbulence, MotionFieldset, MotionFigcaption, MotionFigure, MotionFilter, MotionFooter, MotionForeignObject, MotionForm, MotionG, MotionH1, MotionH2, MotionH3, MotionH4, MotionH5, MotionH6, MotionHead, MotionHeader, MotionHgroup, MotionHr, MotionHtml, MotionI, MotionIframe, MotionImage, MotionImg, MotionInput, MotionIns, MotionKbd, MotionKeygen, MotionLabel, MotionLegend, MotionLi, MotionLine, MotionLinearGradient, MotionLink, MotionMain, MotionMap, MotionMark, MotionMarker, MotionMask, MotionMenu, MotionMenuitem, MotionMetadata, MotionMeter, MotionNav, MotionObject, MotionOl, MotionOptgroup, MotionOption, MotionOutput, MotionP, MotionParam, MotionPath, MotionPattern, MotionPicture, MotionPolygon, MotionPolyline, MotionPre, MotionProgress, MotionQ, MotionRadialGradient, MotionRect, MotionRp, MotionRt, MotionRuby, MotionS, MotionSamp, MotionScript, MotionSection, MotionSelect, MotionSmall, MotionSource, MotionSpan, MotionStop, MotionStrong, MotionStyle, MotionSub, MotionSummary, MotionSup, MotionSvg, MotionSymbol, MotionTable, MotionTbody, MotionTd, MotionText, MotionTextPath, MotionTextarea, MotionTfoot, MotionTh, MotionThead, MotionTime, MotionTitle, MotionTr, MotionTrack, MotionTspan, MotionU, MotionUl, MotionUse, MotionVideo, MotionView, MotionWbr, MotionWebview }; diff --git a/node_modules/framer-motion/dist/es/render/components/m/proxy.mjs b/node_modules/framer-motion/dist/es/render/components/m/proxy.mjs new file mode 100644 index 00000000..35a075ff --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/m/proxy.mjs @@ -0,0 +1,6 @@ +import { createDOMMotionComponentProxy } from '../create-proxy.mjs'; +import { createMinimalMotionComponent } from './create.mjs'; + +const m = /*@__PURE__*/ createDOMMotionComponentProxy(createMinimalMotionComponent); + +export { m }; diff --git a/node_modules/framer-motion/dist/es/render/components/motion/create.mjs b/node_modules/framer-motion/dist/es/render/components/motion/create.mjs new file mode 100644 index 00000000..3adc16ac --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/motion/create.mjs @@ -0,0 +1,15 @@ +import { animations } from '../../../motion/features/animations.mjs'; +import { drag } from '../../../motion/features/drag.mjs'; +import { gestureAnimations } from '../../../motion/features/gestures.mjs'; +import { layout } from '../../../motion/features/layout.mjs'; +import { createMotionComponentFactory } from '../create-factory.mjs'; +import { createDomVisualElement } from '../../dom/create-visual-element.mjs'; + +const createMotionComponent = /*@__PURE__*/ createMotionComponentFactory({ + ...animations, + ...gestureAnimations, + ...drag, + ...layout, +}, createDomVisualElement); + +export { createMotionComponent }; diff --git a/node_modules/framer-motion/dist/es/render/components/motion/elements.mjs b/node_modules/framer-motion/dist/es/render/components/motion/elements.mjs new file mode 100644 index 00000000..d83f1516 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/motion/elements.mjs @@ -0,0 +1,194 @@ +"use client"; +import { createMotionComponent } from './create.mjs'; + +/** + * HTML components + */ +const MotionA = /*@__PURE__*/ createMotionComponent("a"); +const MotionAbbr = /*@__PURE__*/ createMotionComponent("abbr"); +const MotionAddress = /*@__PURE__*/ createMotionComponent("address"); +const MotionArea = /*@__PURE__*/ createMotionComponent("area"); +const MotionArticle = /*@__PURE__*/ createMotionComponent("article"); +const MotionAside = /*@__PURE__*/ createMotionComponent("aside"); +const MotionAudio = /*@__PURE__*/ createMotionComponent("audio"); +const MotionB = /*@__PURE__*/ createMotionComponent("b"); +const MotionBase = /*@__PURE__*/ createMotionComponent("base"); +const MotionBdi = /*@__PURE__*/ createMotionComponent("bdi"); +const MotionBdo = /*@__PURE__*/ createMotionComponent("bdo"); +const MotionBig = /*@__PURE__*/ createMotionComponent("big"); +const MotionBlockquote = +/*@__PURE__*/ createMotionComponent("blockquote"); +const MotionBody = /*@__PURE__*/ createMotionComponent("body"); +const MotionButton = /*@__PURE__*/ createMotionComponent("button"); +const MotionCanvas = /*@__PURE__*/ createMotionComponent("canvas"); +const MotionCaption = /*@__PURE__*/ createMotionComponent("caption"); +const MotionCite = /*@__PURE__*/ createMotionComponent("cite"); +const MotionCode = /*@__PURE__*/ createMotionComponent("code"); +const MotionCol = /*@__PURE__*/ createMotionComponent("col"); +const MotionColgroup = /*@__PURE__*/ createMotionComponent("colgroup"); +const MotionData = /*@__PURE__*/ createMotionComponent("data"); +const MotionDatalist = /*@__PURE__*/ createMotionComponent("datalist"); +const MotionDd = /*@__PURE__*/ createMotionComponent("dd"); +const MotionDel = /*@__PURE__*/ createMotionComponent("del"); +const MotionDetails = /*@__PURE__*/ createMotionComponent("details"); +const MotionDfn = /*@__PURE__*/ createMotionComponent("dfn"); +const MotionDialog = /*@__PURE__*/ createMotionComponent("dialog"); +const MotionDiv = /*@__PURE__*/ createMotionComponent("div"); +const MotionDl = /*@__PURE__*/ createMotionComponent("dl"); +const MotionDt = /*@__PURE__*/ createMotionComponent("dt"); +const MotionEm = /*@__PURE__*/ createMotionComponent("em"); +const MotionEmbed = /*@__PURE__*/ createMotionComponent("embed"); +const MotionFieldset = /*@__PURE__*/ createMotionComponent("fieldset"); +const MotionFigcaption = +/*@__PURE__*/ createMotionComponent("figcaption"); +const MotionFigure = /*@__PURE__*/ createMotionComponent("figure"); +const MotionFooter = /*@__PURE__*/ createMotionComponent("footer"); +const MotionForm = /*@__PURE__*/ createMotionComponent("form"); +const MotionH1 = /*@__PURE__*/ createMotionComponent("h1"); +const MotionH2 = /*@__PURE__*/ createMotionComponent("h2"); +const MotionH3 = /*@__PURE__*/ createMotionComponent("h3"); +const MotionH4 = /*@__PURE__*/ createMotionComponent("h4"); +const MotionH5 = /*@__PURE__*/ createMotionComponent("h5"); +const MotionH6 = /*@__PURE__*/ createMotionComponent("h6"); +const MotionHead = /*@__PURE__*/ createMotionComponent("head"); +const MotionHeader = /*@__PURE__*/ createMotionComponent("header"); +const MotionHgroup = /*@__PURE__*/ createMotionComponent("hgroup"); +const MotionHr = /*@__PURE__*/ createMotionComponent("hr"); +const MotionHtml = /*@__PURE__*/ createMotionComponent("html"); +const MotionI = /*@__PURE__*/ createMotionComponent("i"); +const MotionIframe = /*@__PURE__*/ createMotionComponent("iframe"); +const MotionImg = /*@__PURE__*/ createMotionComponent("img"); +const MotionInput = /*@__PURE__*/ createMotionComponent("input"); +const MotionIns = /*@__PURE__*/ createMotionComponent("ins"); +const MotionKbd = /*@__PURE__*/ createMotionComponent("kbd"); +const MotionKeygen = /*@__PURE__*/ createMotionComponent("keygen"); +const MotionLabel = /*@__PURE__*/ createMotionComponent("label"); +const MotionLegend = /*@__PURE__*/ createMotionComponent("legend"); +const MotionLi = /*@__PURE__*/ createMotionComponent("li"); +const MotionLink = /*@__PURE__*/ createMotionComponent("link"); +const MotionMain = /*@__PURE__*/ createMotionComponent("main"); +const MotionMap = /*@__PURE__*/ createMotionComponent("map"); +const MotionMark = /*@__PURE__*/ createMotionComponent("mark"); +const MotionMenu = /*@__PURE__*/ createMotionComponent("menu"); +const MotionMenuitem = /*@__PURE__*/ createMotionComponent("menuitem"); +const MotionMeter = /*@__PURE__*/ createMotionComponent("meter"); +const MotionNav = /*@__PURE__*/ createMotionComponent("nav"); +const MotionObject = /*@__PURE__*/ createMotionComponent("object"); +const MotionOl = /*@__PURE__*/ createMotionComponent("ol"); +const MotionOptgroup = /*@__PURE__*/ createMotionComponent("optgroup"); +const MotionOption = /*@__PURE__*/ createMotionComponent("option"); +const MotionOutput = /*@__PURE__*/ createMotionComponent("output"); +const MotionP = /*@__PURE__*/ createMotionComponent("p"); +const MotionParam = /*@__PURE__*/ createMotionComponent("param"); +const MotionPicture = /*@__PURE__*/ createMotionComponent("picture"); +const MotionPre = /*@__PURE__*/ createMotionComponent("pre"); +const MotionProgress = /*@__PURE__*/ createMotionComponent("progress"); +const MotionQ = /*@__PURE__*/ createMotionComponent("q"); +const MotionRp = /*@__PURE__*/ createMotionComponent("rp"); +const MotionRt = /*@__PURE__*/ createMotionComponent("rt"); +const MotionRuby = /*@__PURE__*/ createMotionComponent("ruby"); +const MotionS = /*@__PURE__*/ createMotionComponent("s"); +const MotionSamp = /*@__PURE__*/ createMotionComponent("samp"); +const MotionScript = /*@__PURE__*/ createMotionComponent("script"); +const MotionSection = /*@__PURE__*/ createMotionComponent("section"); +const MotionSelect = /*@__PURE__*/ createMotionComponent("select"); +const MotionSmall = /*@__PURE__*/ createMotionComponent("small"); +const MotionSource = /*@__PURE__*/ createMotionComponent("source"); +const MotionSpan = /*@__PURE__*/ createMotionComponent("span"); +const MotionStrong = /*@__PURE__*/ createMotionComponent("strong"); +const MotionStyle = /*@__PURE__*/ createMotionComponent("style"); +const MotionSub = /*@__PURE__*/ createMotionComponent("sub"); +const MotionSummary = /*@__PURE__*/ createMotionComponent("summary"); +const MotionSup = /*@__PURE__*/ createMotionComponent("sup"); +const MotionTable = /*@__PURE__*/ createMotionComponent("table"); +const MotionTbody = /*@__PURE__*/ createMotionComponent("tbody"); +const MotionTd = /*@__PURE__*/ createMotionComponent("td"); +const MotionTextarea = /*@__PURE__*/ createMotionComponent("textarea"); +const MotionTfoot = /*@__PURE__*/ createMotionComponent("tfoot"); +const MotionTh = /*@__PURE__*/ createMotionComponent("th"); +const MotionThead = /*@__PURE__*/ createMotionComponent("thead"); +const MotionTime = /*@__PURE__*/ createMotionComponent("time"); +const MotionTitle = /*@__PURE__*/ createMotionComponent("title"); +const MotionTr = /*@__PURE__*/ createMotionComponent("tr"); +const MotionTrack = /*@__PURE__*/ createMotionComponent("track"); +const MotionU = /*@__PURE__*/ createMotionComponent("u"); +const MotionUl = /*@__PURE__*/ createMotionComponent("ul"); +const MotionVideo = /*@__PURE__*/ createMotionComponent("video"); +const MotionWbr = /*@__PURE__*/ createMotionComponent("wbr"); +const MotionWebview = /*@__PURE__*/ createMotionComponent("webview"); +/** + * SVG components + */ +const MotionAnimate = /*@__PURE__*/ createMotionComponent("animate"); +const MotionCircle = /*@__PURE__*/ createMotionComponent("circle"); +const MotionDefs = /*@__PURE__*/ createMotionComponent("defs"); +const MotionDesc = /*@__PURE__*/ createMotionComponent("desc"); +const MotionEllipse = /*@__PURE__*/ createMotionComponent("ellipse"); +const MotionG = /*@__PURE__*/ createMotionComponent("g"); +const MotionImage = /*@__PURE__*/ createMotionComponent("image"); +const MotionLine = /*@__PURE__*/ createMotionComponent("line"); +const MotionFilter = /*@__PURE__*/ createMotionComponent("filter"); +const MotionMarker = /*@__PURE__*/ createMotionComponent("marker"); +const MotionMask = /*@__PURE__*/ createMotionComponent("mask"); +const MotionMetadata = /*@__PURE__*/ createMotionComponent("metadata"); +const MotionPath = /*@__PURE__*/ createMotionComponent("path"); +const MotionPattern = /*@__PURE__*/ createMotionComponent("pattern"); +const MotionPolygon = /*@__PURE__*/ createMotionComponent("polygon"); +const MotionPolyline = /*@__PURE__*/ createMotionComponent("polyline"); +const MotionRect = /*@__PURE__*/ createMotionComponent("rect"); +const MotionStop = /*@__PURE__*/ createMotionComponent("stop"); +const MotionSvg = /*@__PURE__*/ createMotionComponent("svg"); +const MotionSymbol = /*@__PURE__*/ createMotionComponent("symbol"); +const MotionText = /*@__PURE__*/ createMotionComponent("text"); +const MotionTspan = /*@__PURE__*/ createMotionComponent("tspan"); +const MotionUse = /*@__PURE__*/ createMotionComponent("use"); +const MotionView = /*@__PURE__*/ createMotionComponent("view"); +const MotionClipPath = /*@__PURE__*/ createMotionComponent("clipPath"); +const MotionFeBlend = /*@__PURE__*/ createMotionComponent("feBlend"); +const MotionFeColorMatrix = +/*@__PURE__*/ createMotionComponent("feColorMatrix"); +const MotionFeComponentTransfer = /*@__PURE__*/ createMotionComponent("feComponentTransfer"); +const MotionFeComposite = +/*@__PURE__*/ createMotionComponent("feComposite"); +const MotionFeConvolveMatrix = +/*@__PURE__*/ createMotionComponent("feConvolveMatrix"); +const MotionFeDiffuseLighting = +/*@__PURE__*/ createMotionComponent("feDiffuseLighting"); +const MotionFeDisplacementMap = +/*@__PURE__*/ createMotionComponent("feDisplacementMap"); +const MotionFeDistantLight = +/*@__PURE__*/ createMotionComponent("feDistantLight"); +const MotionFeDropShadow = +/*@__PURE__*/ createMotionComponent("feDropShadow"); +const MotionFeFlood = /*@__PURE__*/ createMotionComponent("feFlood"); +const MotionFeFuncA = /*@__PURE__*/ createMotionComponent("feFuncA"); +const MotionFeFuncB = /*@__PURE__*/ createMotionComponent("feFuncB"); +const MotionFeFuncG = /*@__PURE__*/ createMotionComponent("feFuncG"); +const MotionFeFuncR = /*@__PURE__*/ createMotionComponent("feFuncR"); +const MotionFeGaussianBlur = +/*@__PURE__*/ createMotionComponent("feGaussianBlur"); +const MotionFeImage = /*@__PURE__*/ createMotionComponent("feImage"); +const MotionFeMerge = /*@__PURE__*/ createMotionComponent("feMerge"); +const MotionFeMergeNode = +/*@__PURE__*/ createMotionComponent("feMergeNode"); +const MotionFeMorphology = +/*@__PURE__*/ createMotionComponent("feMorphology"); +const MotionFeOffset = /*@__PURE__*/ createMotionComponent("feOffset"); +const MotionFePointLight = +/*@__PURE__*/ createMotionComponent("fePointLight"); +const MotionFeSpecularLighting = +/*@__PURE__*/ createMotionComponent("feSpecularLighting"); +const MotionFeSpotLight = +/*@__PURE__*/ createMotionComponent("feSpotLight"); +const MotionFeTile = /*@__PURE__*/ createMotionComponent("feTile"); +const MotionFeTurbulence = +/*@__PURE__*/ createMotionComponent("feTurbulence"); +const MotionForeignObject = +/*@__PURE__*/ createMotionComponent("foreignObject"); +const MotionLinearGradient = +/*@__PURE__*/ createMotionComponent("linearGradient"); +const MotionRadialGradient = +/*@__PURE__*/ createMotionComponent("radialGradient"); +const MotionTextPath = /*@__PURE__*/ createMotionComponent("textPath"); + +export { MotionA, MotionAbbr, MotionAddress, MotionAnimate, MotionArea, MotionArticle, MotionAside, MotionAudio, MotionB, MotionBase, MotionBdi, MotionBdo, MotionBig, MotionBlockquote, MotionBody, MotionButton, MotionCanvas, MotionCaption, MotionCircle, MotionCite, MotionClipPath, MotionCode, MotionCol, MotionColgroup, MotionData, MotionDatalist, MotionDd, MotionDefs, MotionDel, MotionDesc, MotionDetails, MotionDfn, MotionDialog, MotionDiv, MotionDl, MotionDt, MotionEllipse, MotionEm, MotionEmbed, MotionFeBlend, MotionFeColorMatrix, MotionFeComponentTransfer, MotionFeComposite, MotionFeConvolveMatrix, MotionFeDiffuseLighting, MotionFeDisplacementMap, MotionFeDistantLight, MotionFeDropShadow, MotionFeFlood, MotionFeFuncA, MotionFeFuncB, MotionFeFuncG, MotionFeFuncR, MotionFeGaussianBlur, MotionFeImage, MotionFeMerge, MotionFeMergeNode, MotionFeMorphology, MotionFeOffset, MotionFePointLight, MotionFeSpecularLighting, MotionFeSpotLight, MotionFeTile, MotionFeTurbulence, MotionFieldset, MotionFigcaption, MotionFigure, MotionFilter, MotionFooter, MotionForeignObject, MotionForm, MotionG, MotionH1, MotionH2, MotionH3, MotionH4, MotionH5, MotionH6, MotionHead, MotionHeader, MotionHgroup, MotionHr, MotionHtml, MotionI, MotionIframe, MotionImage, MotionImg, MotionInput, MotionIns, MotionKbd, MotionKeygen, MotionLabel, MotionLegend, MotionLi, MotionLine, MotionLinearGradient, MotionLink, MotionMain, MotionMap, MotionMark, MotionMarker, MotionMask, MotionMenu, MotionMenuitem, MotionMetadata, MotionMeter, MotionNav, MotionObject, MotionOl, MotionOptgroup, MotionOption, MotionOutput, MotionP, MotionParam, MotionPath, MotionPattern, MotionPicture, MotionPolygon, MotionPolyline, MotionPre, MotionProgress, MotionQ, MotionRadialGradient, MotionRect, MotionRp, MotionRt, MotionRuby, MotionS, MotionSamp, MotionScript, MotionSection, MotionSelect, MotionSmall, MotionSource, MotionSpan, MotionStop, MotionStrong, MotionStyle, MotionSub, MotionSummary, MotionSup, MotionSvg, MotionSymbol, MotionTable, MotionTbody, MotionTd, MotionText, MotionTextPath, MotionTextarea, MotionTfoot, MotionTh, MotionThead, MotionTime, MotionTitle, MotionTr, MotionTrack, MotionTspan, MotionU, MotionUl, MotionUse, MotionVideo, MotionView, MotionWbr, MotionWebview }; diff --git a/node_modules/framer-motion/dist/es/render/components/motion/proxy.mjs b/node_modules/framer-motion/dist/es/render/components/motion/proxy.mjs new file mode 100644 index 00000000..a1de016d --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/components/motion/proxy.mjs @@ -0,0 +1,6 @@ +import { createDOMMotionComponentProxy } from '../create-proxy.mjs'; +import { createMotionComponent } from './create.mjs'; + +const motion = /*@__PURE__*/ createDOMMotionComponentProxy(createMotionComponent); + +export { motion }; diff --git a/node_modules/framer-motion/dist/es/render/dom/DOMVisualElement.mjs b/node_modules/framer-motion/dist/es/render/dom/DOMVisualElement.mjs new file mode 100644 index 00000000..b7f9d0de --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/DOMVisualElement.mjs @@ -0,0 +1,42 @@ +import { DOMKeyframesResolver, isMotionValue } from 'motion-dom'; +import { VisualElement } from '../VisualElement.mjs'; + +class DOMVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.KeyframeResolver = DOMKeyframesResolver; + } + sortInstanceNodePosition(a, b) { + /** + * compareDocumentPosition returns a bitmask, by using the bitwise & + * we're returning true if 2 in that bitmask is set to true. 2 is set + * to true if b preceeds a. + */ + return a.compareDocumentPosition(b) & 2 ? 1 : -1; + } + getBaseTargetFromProps(props, key) { + return props.style + ? props.style[key] + : undefined; + } + removeValueFromRenderState(key, { vars, style }) { + delete vars[key]; + delete style[key]; + } + handleChildMotionValue() { + if (this.childSubscription) { + this.childSubscription(); + delete this.childSubscription; + } + const { children } = this.props; + if (isMotionValue(children)) { + this.childSubscription = children.on("change", (latest) => { + if (this.current) { + this.current.textContent = `${latest}`; + } + }); + } + } +} + +export { DOMVisualElement }; diff --git a/node_modules/framer-motion/dist/es/render/dom/create-visual-element.mjs b/node_modules/framer-motion/dist/es/render/dom/create-visual-element.mjs new file mode 100644 index 00000000..4bc45bf2 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/create-visual-element.mjs @@ -0,0 +1,14 @@ +import { Fragment } from 'react'; +import { HTMLVisualElement } from '../html/HTMLVisualElement.mjs'; +import { SVGVisualElement } from '../svg/SVGVisualElement.mjs'; +import { isSVGComponent } from './utils/is-svg-component.mjs'; + +const createDomVisualElement = (Component, options) => { + return isSVGComponent(Component) + ? new SVGVisualElement(options) + : new HTMLVisualElement(options, { + allowProjection: Component !== Fragment, + }); +}; + +export { createDomVisualElement }; diff --git a/node_modules/framer-motion/dist/es/render/dom/features-animation.mjs b/node_modules/framer-motion/dist/es/render/dom/features-animation.mjs new file mode 100644 index 00000000..3d1377db --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/features-animation.mjs @@ -0,0 +1,14 @@ +import { animations } from '../../motion/features/animations.mjs'; +import { gestureAnimations } from '../../motion/features/gestures.mjs'; +import { createDomVisualElement } from './create-visual-element.mjs'; + +/** + * @public + */ +const domAnimation = { + renderer: createDomVisualElement, + ...animations, + ...gestureAnimations, +}; + +export { domAnimation }; diff --git a/node_modules/framer-motion/dist/es/render/dom/features-max.mjs b/node_modules/framer-motion/dist/es/render/dom/features-max.mjs new file mode 100644 index 00000000..46d792df --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/features-max.mjs @@ -0,0 +1,14 @@ +import { drag } from '../../motion/features/drag.mjs'; +import { layout } from '../../motion/features/layout.mjs'; +import { domAnimation } from './features-animation.mjs'; + +/** + * @public + */ +const domMax = { + ...domAnimation, + ...drag, + ...layout, +}; + +export { domMax }; diff --git a/node_modules/framer-motion/dist/es/render/dom/features-min.mjs b/node_modules/framer-motion/dist/es/render/dom/features-min.mjs new file mode 100644 index 00000000..c72a12dd --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/features-min.mjs @@ -0,0 +1,12 @@ +import { animations } from '../../motion/features/animations.mjs'; +import { createDomVisualElement } from './create-visual-element.mjs'; + +/** + * @public + */ +const domMin = { + renderer: createDomVisualElement, + ...animations, +}; + +export { domMin }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/attach-animation.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/attach-animation.mjs new file mode 100644 index 00000000..87071605 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/attach-animation.mjs @@ -0,0 +1,17 @@ +import { observeTimeline } from 'motion-dom'; +import { getTimeline } from './utils/get-timeline.mjs'; + +function attachToAnimation(animation, options) { + const timeline = getTimeline(options); + return animation.attachTimeline({ + timeline: options.target ? undefined : timeline, + observe: (valueAnimation) => { + valueAnimation.pause(); + return observeTimeline((progress) => { + valueAnimation.time = valueAnimation.duration * progress; + }, timeline); + }, + }); +} + +export { attachToAnimation }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/attach-function.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/attach-function.mjs new file mode 100644 index 00000000..698e9548 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/attach-function.mjs @@ -0,0 +1,23 @@ +import { observeTimeline } from 'motion-dom'; +import { scrollInfo } from './track.mjs'; +import { getTimeline } from './utils/get-timeline.mjs'; + +/** + * If the onScroll function has two arguments, it's expecting + * more specific information about the scroll from scrollInfo. + */ +function isOnScrollWithInfo(onScroll) { + return onScroll.length === 2; +} +function attachToFunction(onScroll, options) { + if (isOnScrollWithInfo(onScroll)) { + return scrollInfo((info) => { + onScroll(info[options.axis].progress, info); + }, options); + } + else { + return observeTimeline(onScroll, getTimeline(options)); + } +} + +export { attachToFunction }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/index.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/index.mjs new file mode 100644 index 00000000..008d6cd3 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/index.mjs @@ -0,0 +1,14 @@ +import { noop } from 'motion-utils'; +import { attachToAnimation } from './attach-animation.mjs'; +import { attachToFunction } from './attach-function.mjs'; + +function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) { + if (!container) + return noop; + const optionsWithDefaults = { axis, container, ...options }; + return typeof onScroll === "function" + ? attachToFunction(onScroll, optionsWithDefaults) + : attachToAnimation(onScroll, optionsWithDefaults); +} + +export { scroll }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/info.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/info.mjs new file mode 100644 index 00000000..d199dbc7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/info.mjs @@ -0,0 +1,55 @@ +import { progress, velocityPerSecond } from 'motion-utils'; + +/** + * A time in milliseconds, beyond which we consider the scroll velocity to be 0. + */ +const maxElapsed = 50; +const createAxisInfo = () => ({ + current: 0, + offset: [], + progress: 0, + scrollLength: 0, + targetOffset: 0, + targetLength: 0, + containerLength: 0, + velocity: 0, +}); +const createScrollInfo = () => ({ + time: 0, + x: createAxisInfo(), + y: createAxisInfo(), +}); +const keys = { + x: { + length: "Width", + position: "Left", + }, + y: { + length: "Height", + position: "Top", + }, +}; +function updateAxisInfo(element, axisName, info, time) { + const axis = info[axisName]; + const { length, position } = keys[axisName]; + const prev = axis.current; + const prevTime = info.time; + axis.current = element[`scroll${position}`]; + axis.scrollLength = element[`scroll${length}`] - element[`client${length}`]; + axis.offset.length = 0; + axis.offset[0] = 0; + axis.offset[1] = axis.scrollLength; + axis.progress = progress(0, axis.scrollLength, axis.current); + const elapsed = time - prevTime; + axis.velocity = + elapsed > maxElapsed + ? 0 + : velocityPerSecond(axis.current - prev, elapsed); +} +function updateScrollInfo(element, info, time) { + updateAxisInfo(element, "x", info, time); + updateAxisInfo(element, "y", info, time); + info.time = time; +} + +export { createScrollInfo, updateScrollInfo }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/edge.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/edge.mjs new file mode 100644 index 00000000..9ac6bb2c --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/edge.mjs @@ -0,0 +1,45 @@ +const namedEdges = { + start: 0, + center: 0.5, + end: 1, +}; +function resolveEdge(edge, length, inset = 0) { + let delta = 0; + /** + * If we have this edge defined as a preset, replace the definition + * with the numerical value. + */ + if (edge in namedEdges) { + edge = namedEdges[edge]; + } + /** + * Handle unit values + */ + if (typeof edge === "string") { + const asNumber = parseFloat(edge); + if (edge.endsWith("px")) { + delta = asNumber; + } + else if (edge.endsWith("%")) { + edge = asNumber / 100; + } + else if (edge.endsWith("vw")) { + delta = (asNumber / 100) * document.documentElement.clientWidth; + } + else if (edge.endsWith("vh")) { + delta = (asNumber / 100) * document.documentElement.clientHeight; + } + else { + edge = asNumber; + } + } + /** + * If the edge is defined as a number, handle as a progress value. + */ + if (typeof edge === "number") { + delta = length * edge; + } + return inset + delta; +} + +export { namedEdges, resolveEdge }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/index.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/index.mjs new file mode 100644 index 00000000..9576fa09 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/index.mjs @@ -0,0 +1,59 @@ +import { interpolate, defaultOffset } from 'motion-dom'; +import { clamp } from 'motion-utils'; +import { calcInset } from './inset.mjs'; +import { resolveOffset } from './offset.mjs'; +import { ScrollOffset } from './presets.mjs'; + +const point = { x: 0, y: 0 }; +function getTargetSize(target) { + return "getBBox" in target && target.tagName !== "svg" + ? target.getBBox() + : { width: target.clientWidth, height: target.clientHeight }; +} +function resolveOffsets(container, info, options) { + const { offset: offsetDefinition = ScrollOffset.All } = options; + const { target = container, axis = "y" } = options; + const lengthLabel = axis === "y" ? "height" : "width"; + const inset = target !== container ? calcInset(target, container) : point; + /** + * Measure the target and container. If they're the same thing then we + * use the container's scrollWidth/Height as the target, from there + * all other calculations can remain the same. + */ + const targetSize = target === container + ? { width: container.scrollWidth, height: container.scrollHeight } + : getTargetSize(target); + const containerSize = { + width: container.clientWidth, + height: container.clientHeight, + }; + /** + * Reset the length of the resolved offset array rather than creating a new one. + * TODO: More reusable data structures for targetSize/containerSize would also be good. + */ + info[axis].offset.length = 0; + /** + * Populate the offset array by resolving the user's offset definition into + * a list of pixel scroll offets. + */ + let hasChanged = !info[axis].interpolate; + const numOffsets = offsetDefinition.length; + for (let i = 0; i < numOffsets; i++) { + const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]); + if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) { + hasChanged = true; + } + info[axis].offset[i] = offset; + } + /** + * If the pixel scroll offsets have changed, create a new interpolator function + * to map scroll value into a progress. + */ + if (hasChanged) { + info[axis].interpolate = interpolate(info[axis].offset, defaultOffset(offsetDefinition), { clamp: false }); + info[axis].interpolatorOffsets = [...info[axis].offset]; + } + info[axis].progress = clamp(0, 1, info[axis].interpolate(info[axis].current)); +} + +export { resolveOffsets }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/inset.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/inset.mjs new file mode 100644 index 00000000..70e8315b --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/inset.mjs @@ -0,0 +1,47 @@ +import { isHTMLElement } from 'motion-dom'; + +function calcInset(element, container) { + const inset = { x: 0, y: 0 }; + let current = element; + while (current && current !== container) { + if (isHTMLElement(current)) { + inset.x += current.offsetLeft; + inset.y += current.offsetTop; + current = current.offsetParent; + } + else if (current.tagName === "svg") { + /** + * This isn't an ideal approach to measuring the offset of tags. + * It would be preferable, given they behave like HTMLElements in most ways + * to use offsetLeft/Top. But these don't exist on . Likewise we + * can't use .getBBox() like most SVG elements as these provide the offset + * relative to the SVG itself, which for is usually 0x0. + */ + const svgBoundingBox = current.getBoundingClientRect(); + current = current.parentElement; + const parentBoundingBox = current.getBoundingClientRect(); + inset.x += svgBoundingBox.left - parentBoundingBox.left; + inset.y += svgBoundingBox.top - parentBoundingBox.top; + } + else if (current instanceof SVGGraphicsElement) { + const { x, y } = current.getBBox(); + inset.x += x; + inset.y += y; + let svg = null; + let parent = current.parentNode; + while (!svg) { + if (parent.tagName === "svg") { + svg = parent; + } + parent = current.parentNode; + } + current = svg; + } + else { + break; + } + } + return inset; +} + +export { calcInset }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/offset.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/offset.mjs new file mode 100644 index 00000000..3d4256f6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/offset.mjs @@ -0,0 +1,35 @@ +import { resolveEdge, namedEdges } from './edge.mjs'; + +const defaultOffset = [0, 0]; +function resolveOffset(offset, containerLength, targetLength, targetInset) { + let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset; + let targetPoint = 0; + let containerPoint = 0; + if (typeof offset === "number") { + /** + * If we're provided offset: [0, 0.5, 1] then each number x should become + * [x, x], so we default to the behaviour of mapping 0 => 0 of both target + * and container etc. + */ + offsetDefinition = [offset, offset]; + } + else if (typeof offset === "string") { + offset = offset.trim(); + if (offset.includes(" ")) { + offsetDefinition = offset.split(" "); + } + else { + /** + * If we're provided a definition like "100px" then we want to apply + * that only to the top of the target point, leaving the container at 0. + * Whereas a named offset like "end" should be applied to both. + */ + offsetDefinition = [offset, namedEdges[offset] ? offset : `0`]; + } + } + targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset); + containerPoint = resolveEdge(offsetDefinition[1], containerLength); + return targetPoint - containerPoint; +} + +export { resolveOffset }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/presets.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/presets.mjs new file mode 100644 index 00000000..6759ebba --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/offsets/presets.mjs @@ -0,0 +1,20 @@ +const ScrollOffset = { + Enter: [ + [0, 1], + [1, 1], + ], + Exit: [ + [0, 0], + [1, 0], + ], + Any: [ + [1, 0], + [0, 1], + ], + All: [ + [0, 0], + [1, 1], + ], +}; + +export { ScrollOffset }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/on-scroll-handler.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/on-scroll-handler.mjs new file mode 100644 index 00000000..6053f1a0 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/on-scroll-handler.mjs @@ -0,0 +1,48 @@ +import { warnOnce } from 'motion-utils'; +import { updateScrollInfo } from './info.mjs'; +import { resolveOffsets } from './offsets/index.mjs'; + +function measure(container, target = container, info) { + /** + * Find inset of target within scrollable container + */ + info.x.targetOffset = 0; + info.y.targetOffset = 0; + if (target !== container) { + let node = target; + while (node && node !== container) { + info.x.targetOffset += node.offsetLeft; + info.y.targetOffset += node.offsetTop; + node = node.offsetParent; + } + } + info.x.targetLength = + target === container ? target.scrollWidth : target.clientWidth; + info.y.targetLength = + target === container ? target.scrollHeight : target.clientHeight; + info.x.containerLength = container.clientWidth; + info.y.containerLength = container.clientHeight; + /** + * In development mode ensure scroll containers aren't position: static as this makes + * it difficult to measure their relative positions. + */ + if (process.env.NODE_ENV !== "production") { + if (container && target && target !== container) { + warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly."); + } + } +} +function createOnScrollHandler(element, onScroll, info, options = {}) { + return { + measure: (time) => { + measure(element, options.target, info); + updateScrollInfo(element, info, time); + if (options.offset || options.target) { + resolveOffsets(element, info, options); + } + }, + notify: () => onScroll(info), + }; +} + +export { createOnScrollHandler }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/track.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/track.mjs new file mode 100644 index 00000000..be20ac63 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/track.mjs @@ -0,0 +1,80 @@ +import { resize, frame, cancelFrame, frameData } from 'motion-dom'; +import { noop } from 'motion-utils'; +import { createScrollInfo } from './info.mjs'; +import { createOnScrollHandler } from './on-scroll-handler.mjs'; + +const scrollListeners = new WeakMap(); +const resizeListeners = new WeakMap(); +const onScrollHandlers = new WeakMap(); +const getEventTarget = (element) => element === document.scrollingElement ? window : element; +function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) { + if (!container) + return noop; + let containerHandlers = onScrollHandlers.get(container); + /** + * Get the onScroll handlers for this container. + * If one isn't found, create a new one. + */ + if (!containerHandlers) { + containerHandlers = new Set(); + onScrollHandlers.set(container, containerHandlers); + } + /** + * Create a new onScroll handler for the provided callback. + */ + const info = createScrollInfo(); + const containerHandler = createOnScrollHandler(container, onScroll, info, options); + containerHandlers.add(containerHandler); + /** + * Check if there's a scroll event listener for this container. + * If not, create one. + */ + if (!scrollListeners.has(container)) { + const measureAll = () => { + for (const handler of containerHandlers) { + handler.measure(frameData.timestamp); + } + frame.preUpdate(notifyAll); + }; + const notifyAll = () => { + for (const handler of containerHandlers) { + handler.notify(); + } + }; + const listener = () => frame.read(measureAll); + scrollListeners.set(container, listener); + const target = getEventTarget(container); + window.addEventListener("resize", listener, { passive: true }); + if (container !== document.documentElement) { + resizeListeners.set(container, resize(container, listener)); + } + target.addEventListener("scroll", listener, { passive: true }); + listener(); + } + const listener = scrollListeners.get(container); + frame.read(listener, false, true); + return () => { + cancelFrame(listener); + /** + * Check if we even have any handlers for this container. + */ + const currentHandlers = onScrollHandlers.get(container); + if (!currentHandlers) + return; + currentHandlers.delete(containerHandler); + if (currentHandlers.size) + return; + /** + * If no more handlers, remove the scroll listener too. + */ + const scrollListener = scrollListeners.get(container); + scrollListeners.delete(container); + if (scrollListener) { + getEventTarget(container).removeEventListener("scroll", scrollListener); + resizeListeners.get(container)?.(); + window.removeEventListener("resize", scrollListener); + } + }; +} + +export { scrollInfo }; diff --git a/node_modules/framer-motion/dist/es/render/dom/scroll/utils/get-timeline.mjs b/node_modules/framer-motion/dist/es/render/dom/scroll/utils/get-timeline.mjs new file mode 100644 index 00000000..b7702124 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/scroll/utils/get-timeline.mjs @@ -0,0 +1,30 @@ +import { supportsScrollTimeline } from 'motion-dom'; +import { scrollInfo } from '../track.mjs'; + +const timelineCache = new Map(); +function scrollTimelineFallback(options) { + const currentTime = { value: 0 }; + const cancel = scrollInfo((info) => { + currentTime.value = info[options.axis].progress * 100; + }, options); + return { currentTime, cancel }; +} +function getTimeline({ source, container, ...options }) { + const { axis } = options; + if (source) + container = source; + const containerCache = timelineCache.get(container) ?? new Map(); + timelineCache.set(container, containerCache); + const targetKey = options.target ?? "self"; + const targetCache = containerCache.get(targetKey) ?? {}; + const axisKey = axis + (options.offset ?? []).join(","); + if (!targetCache[axisKey]) { + targetCache[axisKey] = + !options.target && supportsScrollTimeline() + ? new ScrollTimeline({ source: container, axis }) + : scrollTimelineFallback({ container, ...options }); + } + return targetCache[axisKey]; +} + +export { getTimeline }; diff --git a/node_modules/framer-motion/dist/es/render/dom/use-render.mjs b/node_modules/framer-motion/dist/es/render/dom/use-render.mjs new file mode 100644 index 00000000..e3311ae2 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/use-render.mjs @@ -0,0 +1,33 @@ +import { isMotionValue } from 'motion-dom'; +import { Fragment, useMemo, createElement } from 'react'; +import { useHTMLProps } from '../html/use-props.mjs'; +import { useSVGProps } from '../svg/use-props.mjs'; +import { filterProps } from './utils/filter-props.mjs'; +import { isSVGComponent } from './utils/is-svg-component.mjs'; + +function createUseRender(forwardMotionProps = false) { + const useRender = (Component, props, ref, { latestValues }, isStatic) => { + const useVisualProps = isSVGComponent(Component) + ? useSVGProps + : useHTMLProps; + const visualProps = useVisualProps(props, latestValues, isStatic, Component); + const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps); + const elementProps = Component !== Fragment + ? { ...filteredProps, ...visualProps, ref } + : {}; + /** + * If component has been handed a motion value as its child, + * memoise its initial value and render that. Subsequent updates + * will be handled by the onChange handler + */ + const { children } = props; + const renderedChildren = useMemo(() => (isMotionValue(children) ? children.get() : children), [children]); + return createElement(Component, { + ...elementProps, + children: renderedChildren, + }); + }; + return useRender; +} + +export { createUseRender }; diff --git a/node_modules/framer-motion/dist/es/render/dom/utils/camel-to-dash.mjs b/node_modules/framer-motion/dist/es/render/dom/utils/camel-to-dash.mjs new file mode 100644 index 00000000..436d5ab5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/utils/camel-to-dash.mjs @@ -0,0 +1,6 @@ +/** + * Convert camelCase to dash-case properties. + */ +const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase(); + +export { camelToDash }; diff --git a/node_modules/framer-motion/dist/es/render/dom/utils/filter-props.mjs b/node_modules/framer-motion/dist/es/render/dom/utils/filter-props.mjs new file mode 100644 index 00000000..d9f461fa --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/utils/filter-props.mjs @@ -0,0 +1,59 @@ +import { isValidMotionProp } from '../../../motion/utils/valid-prop.mjs'; + +let shouldForward = (key) => !isValidMotionProp(key); +function loadExternalIsValidProp(isValidProp) { + if (typeof isValidProp !== "function") + return; + // Explicitly filter our events + shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key); +} +/** + * Emotion and Styled Components both allow users to pass through arbitrary props to their components + * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which + * of these should be passed to the underlying DOM node. + * + * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props + * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props + * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of + * `@emotion/is-prop-valid`, however to fix this problem we need to use it. + * + * By making it an optionalDependency we can offer this functionality only in the situations where it's + * actually required. + */ +try { + /** + * We attempt to import this package but require won't be defined in esm environments, in that case + * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed + * in favour of explicit injection. + */ + loadExternalIsValidProp(require("@emotion/is-prop-valid").default); +} +catch { + // We don't need to actually do anything here - the fallback is the existing `isPropValid`. +} +function filterProps(props, isDom, forwardMotionProps) { + const filteredProps = {}; + for (const key in props) { + /** + * values is considered a valid prop by Emotion, so if it's present + * this will be rendered out to the DOM unless explicitly filtered. + * + * We check the type as it could be used with the `feColorMatrix` + * element, which we support. + */ + if (key === "values" && typeof props.values === "object") + continue; + if (shouldForward(key) || + (forwardMotionProps === true && isValidMotionProp(key)) || + (!isDom && !isValidMotionProp(key)) || + // If trying to use native HTML drag events, forward drag listeners + (props["draggable"] && + key.startsWith("onDrag"))) { + filteredProps[key] = + props[key]; + } + } + return filteredProps; +} + +export { filterProps, loadExternalIsValidProp }; diff --git a/node_modules/framer-motion/dist/es/render/dom/utils/is-svg-component.mjs b/node_modules/framer-motion/dist/es/render/dom/utils/is-svg-component.mjs new file mode 100644 index 00000000..3ed31f3c --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/utils/is-svg-component.mjs @@ -0,0 +1,30 @@ +import { lowercaseSVGElements } from '../../svg/lowercase-elements.mjs'; + +function isSVGComponent(Component) { + if ( + /** + * If it's not a string, it's a custom React component. Currently we only support + * HTML custom React components. + */ + typeof Component !== "string" || + /** + * If it contains a dash, the element is a custom HTML webcomponent. + */ + Component.includes("-")) { + return false; + } + else if ( + /** + * If it's in our list of lowercase SVG tags, it's an SVG component + */ + lowercaseSVGElements.indexOf(Component) > -1 || + /** + * If it contains a capital letter, it's an SVG component + */ + /[A-Z]/u.test(Component)) { + return true; + } + return false; +} + +export { isSVGComponent }; diff --git a/node_modules/framer-motion/dist/es/render/dom/viewport/index.mjs b/node_modules/framer-motion/dist/es/render/dom/viewport/index.mjs new file mode 100644 index 00000000..ca226c14 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/dom/viewport/index.mjs @@ -0,0 +1,43 @@ +import { resolveElements } from 'motion-dom'; + +const thresholds = { + some: 0, + all: 1, +}; +function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) { + const elements = resolveElements(elementOrSelector); + const activeIntersections = new WeakMap(); + const onIntersectionChange = (entries) => { + entries.forEach((entry) => { + const onEnd = activeIntersections.get(entry.target); + /** + * If there's no change to the intersection, we don't need to + * do anything here. + */ + if (entry.isIntersecting === Boolean(onEnd)) + return; + if (entry.isIntersecting) { + const newOnEnd = onStart(entry.target, entry); + if (typeof newOnEnd === "function") { + activeIntersections.set(entry.target, newOnEnd); + } + else { + observer.unobserve(entry.target); + } + } + else if (typeof onEnd === "function") { + onEnd(entry); + activeIntersections.delete(entry.target); + } + }); + }; + const observer = new IntersectionObserver(onIntersectionChange, { + root, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholds[amount], + }); + elements.forEach((element) => observer.observe(element)); + return () => observer.disconnect(); +} + +export { inView }; diff --git a/node_modules/framer-motion/dist/es/render/html/HTMLVisualElement.mjs b/node_modules/framer-motion/dist/es/render/html/HTMLVisualElement.mjs new file mode 100644 index 00000000..4720f231 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/HTMLVisualElement.mjs @@ -0,0 +1,42 @@ +import { transformProps, defaultTransformValue, readTransformValue, isCSSVariableName } from 'motion-dom'; +import { measureViewportBox } from '../../projection/utils/measure.mjs'; +import { DOMVisualElement } from '../dom/DOMVisualElement.mjs'; +import { buildHTMLStyles } from './utils/build-styles.mjs'; +import { renderHTML } from './utils/render.mjs'; +import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs'; + +function getComputedStyle(element) { + return window.getComputedStyle(element); +} +class HTMLVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "html"; + this.renderInstance = renderHTML; + } + readValueFromInstance(instance, key) { + if (transformProps.has(key)) { + return this.projection?.isProjecting + ? defaultTransformValue(key) + : readTransformValue(instance, key); + } + else { + const computedStyle = getComputedStyle(instance); + const value = (isCSSVariableName(key) + ? computedStyle.getPropertyValue(key) + : computedStyle[key]) || 0; + return typeof value === "string" ? value.trim() : value; + } + } + measureInstanceViewportBox(instance, { transformPagePoint }) { + return measureViewportBox(instance, transformPagePoint); + } + build(renderState, latestValues, props) { + buildHTMLStyles(renderState, latestValues, props.transformTemplate); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps(props, prevProps, visualElement); + } +} + +export { HTMLVisualElement, getComputedStyle }; diff --git a/node_modules/framer-motion/dist/es/render/html/config-motion.mjs b/node_modules/framer-motion/dist/es/render/html/config-motion.mjs new file mode 100644 index 00000000..99b68a46 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/config-motion.mjs @@ -0,0 +1,12 @@ +import { makeUseVisualState } from '../../motion/utils/use-visual-state.mjs'; +import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs'; +import { createHtmlRenderState } from './utils/create-render-state.mjs'; + +const htmlMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps, + createRenderState: createHtmlRenderState, + }), +}; + +export { htmlMotionConfig }; diff --git a/node_modules/framer-motion/dist/es/render/html/use-props.mjs b/node_modules/framer-motion/dist/es/render/html/use-props.mjs new file mode 100644 index 00000000..3ddafbe6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/use-props.mjs @@ -0,0 +1,57 @@ +import { isMotionValue } from 'motion-dom'; +import { useMemo } from 'react'; +import { isForcedMotionValue } from '../../motion/utils/is-forced-motion-value.mjs'; +import { buildHTMLStyles } from './utils/build-styles.mjs'; +import { createHtmlRenderState } from './utils/create-render-state.mjs'; + +function copyRawValuesOnly(target, source, props) { + for (const key in source) { + if (!isMotionValue(source[key]) && !isForcedMotionValue(key, props)) { + target[key] = source[key]; + } + } +} +function useInitialMotionValues({ transformTemplate }, visualState) { + return useMemo(() => { + const state = createHtmlRenderState(); + buildHTMLStyles(state, visualState, transformTemplate); + return Object.assign({}, state.vars, state.style); + }, [visualState]); +} +function useStyle(props, visualState) { + const styleProp = props.style || {}; + const style = {}; + /** + * Copy non-Motion Values straight into style + */ + copyRawValuesOnly(style, styleProp, props); + Object.assign(style, useInitialMotionValues(props, visualState)); + return style; +} +function useHTMLProps(props, visualState) { + // The `any` isn't ideal but it is the type of createElement props argument + const htmlProps = {}; + const style = useStyle(props, visualState); + if (props.drag && props.dragListener !== false) { + // Disable the ghost element when a user drags + htmlProps.draggable = false; + // Disable text selection + style.userSelect = + style.WebkitUserSelect = + style.WebkitTouchCallout = + "none"; + // Disable scrolling on the draggable direction + style.touchAction = + props.drag === true + ? "none" + : `pan-${props.drag === "x" ? "y" : "x"}`; + } + if (props.tabIndex === undefined && + (props.onTap || props.onTapStart || props.whileTap)) { + htmlProps.tabIndex = 0; + } + htmlProps.style = style; + return htmlProps; +} + +export { copyRawValuesOnly, useHTMLProps }; diff --git a/node_modules/framer-motion/dist/es/render/html/utils/build-styles.mjs b/node_modules/framer-motion/dist/es/render/html/utils/build-styles.mjs new file mode 100644 index 00000000..6537d619 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/utils/build-styles.mjs @@ -0,0 +1,62 @@ +import { transformProps, isCSSVariableName, getValueAsType, numberValueTypes } from 'motion-dom'; +import { buildTransform } from './build-transform.mjs'; + +function buildHTMLStyles(state, latestValues, transformTemplate) { + const { style, vars, transformOrigin } = state; + // Track whether we encounter any transform or transformOrigin values. + let hasTransform = false; + let hasTransformOrigin = false; + /** + * Loop over all our latest animated values and decide whether to handle them + * as a style or CSS variable. + * + * Transforms and transform origins are kept separately for further processing. + */ + for (const key in latestValues) { + const value = latestValues[key]; + if (transformProps.has(key)) { + // If this is a transform, flag to enable further transform processing + hasTransform = true; + continue; + } + else if (isCSSVariableName(key)) { + vars[key] = value; + continue; + } + else { + // Convert the value to its default value type, ie 0 -> "0px" + const valueAsType = getValueAsType(value, numberValueTypes[key]); + if (key.startsWith("origin")) { + // If this is a transform origin, flag and enable further transform-origin processing + hasTransformOrigin = true; + transformOrigin[key] = + valueAsType; + } + else { + style[key] = valueAsType; + } + } + } + if (!latestValues.transform) { + if (hasTransform || transformTemplate) { + style.transform = buildTransform(latestValues, state.transform, transformTemplate); + } + else if (style.transform) { + /** + * If we have previously created a transform but currently don't have any, + * reset transform style to none. + */ + style.transform = "none"; + } + } + /** + * Build a transformOrigin style. Uses the same defaults as the browser for + * undefined origins. + */ + if (hasTransformOrigin) { + const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin; + style.transformOrigin = `${originX} ${originY} ${originZ}`; + } +} + +export { buildHTMLStyles }; diff --git a/node_modules/framer-motion/dist/es/render/html/utils/build-transform.mjs b/node_modules/framer-motion/dist/es/render/html/utils/build-transform.mjs new file mode 100644 index 00000000..508b9a4c --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/utils/build-transform.mjs @@ -0,0 +1,60 @@ +import { transformPropOrder, getValueAsType, numberValueTypes } from 'motion-dom'; + +const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", +}; +const numTransforms = transformPropOrder.length; +/** + * Build a CSS transform style from individual x/y/scale etc properties. + * + * This outputs with a default order of transforms/scales/rotations, this can be customised by + * providing a transformTemplate function. + */ +function buildTransform(latestValues, transform, transformTemplate) { + // The transform string we're going to build into. + let transformString = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < numTransforms; i++) { + const key = transformPropOrder[i]; + const value = latestValues[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault || transformTemplate) { + const valueAsType = getValueAsType(value, numberValueTypes[key]); + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + transformString += `${transformName}(${valueAsType}) `; + } + if (transformTemplate) { + transform[key] = valueAsType; + } + } + } + transformString = transformString.trim(); + // If we have a custom `transform` template, pass our transform values and + // generated transformString to that before returning + if (transformTemplate) { + transformString = transformTemplate(transform, transformIsDefault ? "" : transformString); + } + else if (transformIsDefault) { + transformString = "none"; + } + return transformString; +} + +export { buildTransform }; diff --git a/node_modules/framer-motion/dist/es/render/html/utils/create-render-state.mjs b/node_modules/framer-motion/dist/es/render/html/utils/create-render-state.mjs new file mode 100644 index 00000000..39441be6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/utils/create-render-state.mjs @@ -0,0 +1,8 @@ +const createHtmlRenderState = () => ({ + style: {}, + transform: {}, + transformOrigin: {}, + vars: {}, +}); + +export { createHtmlRenderState }; diff --git a/node_modules/framer-motion/dist/es/render/html/utils/render.mjs b/node_modules/framer-motion/dist/es/render/html/utils/render.mjs new file mode 100644 index 00000000..0fe5d808 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/utils/render.mjs @@ -0,0 +1,17 @@ +function renderHTML(element, { style, vars }, styleProp, projection) { + const elementStyle = element.style; + let key; + for (key in style) { + // CSSStyleDeclaration has [index: number]: string; in the types, so we use that as key type. + elementStyle[key] = style[key]; + } + // Write projection styles directly to element style + projection?.applyProjectionStyles(elementStyle, styleProp); + for (key in vars) { + // Loop over any CSS variables and assign those. + // They can only be assigned using `setProperty`. + elementStyle.setProperty(key, vars[key]); + } +} + +export { renderHTML }; diff --git a/node_modules/framer-motion/dist/es/render/html/utils/scrape-motion-values.mjs b/node_modules/framer-motion/dist/es/render/html/utils/scrape-motion-values.mjs new file mode 100644 index 00000000..447dea1e --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/html/utils/scrape-motion-values.mjs @@ -0,0 +1,19 @@ +import { isMotionValue } from 'motion-dom'; +import { isForcedMotionValue } from '../../../motion/utils/is-forced-motion-value.mjs'; + +function scrapeMotionValuesFromProps(props, prevProps, visualElement) { + const { style } = props; + const newValues = {}; + for (const key in style) { + if (isMotionValue(style[key]) || + (prevProps.style && + isMotionValue(prevProps.style[key])) || + isForcedMotionValue(key, props) || + visualElement?.getValue(key)?.liveStyle !== undefined) { + newValues[key] = style[key]; + } + } + return newValues; +} + +export { scrapeMotionValuesFromProps }; diff --git a/node_modules/framer-motion/dist/es/render/object/ObjectVisualElement.mjs b/node_modules/framer-motion/dist/es/render/object/ObjectVisualElement.mjs new file mode 100644 index 00000000..bf467248 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/object/ObjectVisualElement.mjs @@ -0,0 +1,41 @@ +import { createBox } from '../../projection/geometry/models.mjs'; +import { VisualElement } from '../VisualElement.mjs'; + +function isObjectKey(key, object) { + return key in object; +} +class ObjectVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.type = "object"; + } + readValueFromInstance(instance, key) { + if (isObjectKey(key, instance)) { + const value = instance[key]; + if (typeof value === "string" || typeof value === "number") { + return value; + } + } + return undefined; + } + getBaseTargetFromProps() { + return undefined; + } + removeValueFromRenderState(key, renderState) { + delete renderState.output[key]; + } + measureInstanceViewportBox() { + return createBox(); + } + build(renderState, latestValues) { + Object.assign(renderState.output, latestValues); + } + renderInstance(instance, { output }) { + Object.assign(instance, output); + } + sortInstanceNodePosition() { + return 0; + } +} + +export { ObjectVisualElement }; diff --git a/node_modules/framer-motion/dist/es/render/store.mjs b/node_modules/framer-motion/dist/es/render/store.mjs new file mode 100644 index 00000000..c94c04b3 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/store.mjs @@ -0,0 +1,3 @@ +const visualElementStore = new WeakMap(); + +export { visualElementStore }; diff --git a/node_modules/framer-motion/dist/es/render/svg/SVGVisualElement.mjs b/node_modules/framer-motion/dist/es/render/svg/SVGVisualElement.mjs new file mode 100644 index 00000000..f7d24e9d --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/SVGVisualElement.mjs @@ -0,0 +1,44 @@ +import { transformProps, getDefaultValueType } from 'motion-dom'; +import { createBox } from '../../projection/geometry/models.mjs'; +import { DOMVisualElement } from '../dom/DOMVisualElement.mjs'; +import { camelToDash } from '../dom/utils/camel-to-dash.mjs'; +import { buildSVGAttrs } from './utils/build-attrs.mjs'; +import { camelCaseAttributes } from './utils/camel-case-attrs.mjs'; +import { isSVGTag } from './utils/is-svg-tag.mjs'; +import { renderSVG } from './utils/render.mjs'; +import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs'; + +class SVGVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "svg"; + this.isSVGTag = false; + this.measureInstanceViewportBox = createBox; + } + getBaseTargetFromProps(props, key) { + return props[key]; + } + readValueFromInstance(instance, key) { + if (transformProps.has(key)) { + const defaultType = getDefaultValueType(key); + return defaultType ? defaultType.default || 0 : 0; + } + key = !camelCaseAttributes.has(key) ? camelToDash(key) : key; + return instance.getAttribute(key); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps(props, prevProps, visualElement); + } + build(renderState, latestValues, props) { + buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate, props.style); + } + renderInstance(instance, renderState, styleProp, projection) { + renderSVG(instance, renderState, styleProp, projection); + } + mount(instance) { + this.isSVGTag = isSVGTag(instance.tagName); + super.mount(instance); + } +} + +export { SVGVisualElement }; diff --git a/node_modules/framer-motion/dist/es/render/svg/config-motion.mjs b/node_modules/framer-motion/dist/es/render/svg/config-motion.mjs new file mode 100644 index 00000000..7445952a --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/config-motion.mjs @@ -0,0 +1,12 @@ +import { makeUseVisualState } from '../../motion/utils/use-visual-state.mjs'; +import { createSvgRenderState } from './utils/create-render-state.mjs'; +import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs'; + +const svgMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps, + createRenderState: createSvgRenderState, + }), +}; + +export { svgMotionConfig }; diff --git a/node_modules/framer-motion/dist/es/render/svg/lowercase-elements.mjs b/node_modules/framer-motion/dist/es/render/svg/lowercase-elements.mjs new file mode 100644 index 00000000..5435c644 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/lowercase-elements.mjs @@ -0,0 +1,33 @@ +/** + * We keep these listed separately as we use the lowercase tag names as part + * of the runtime bundle to detect SVG components + */ +const lowercaseSVGElements = [ + "animate", + "circle", + "defs", + "desc", + "ellipse", + "g", + "image", + "line", + "filter", + "marker", + "mask", + "metadata", + "path", + "pattern", + "polygon", + "polyline", + "rect", + "stop", + "switch", + "symbol", + "svg", + "text", + "tspan", + "use", + "view", +]; + +export { lowercaseSVGElements }; diff --git a/node_modules/framer-motion/dist/es/render/svg/use-props.mjs b/node_modules/framer-motion/dist/es/render/svg/use-props.mjs new file mode 100644 index 00000000..669baf91 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/use-props.mjs @@ -0,0 +1,24 @@ +import { useMemo } from 'react'; +import { copyRawValuesOnly } from '../html/use-props.mjs'; +import { buildSVGAttrs } from './utils/build-attrs.mjs'; +import { createSvgRenderState } from './utils/create-render-state.mjs'; +import { isSVGTag } from './utils/is-svg-tag.mjs'; + +function useSVGProps(props, visualState, _isStatic, Component) { + const visualProps = useMemo(() => { + const state = createSvgRenderState(); + buildSVGAttrs(state, visualState, isSVGTag(Component), props.transformTemplate, props.style); + return { + ...state.attrs, + style: { ...state.style }, + }; + }, [visualState]); + if (props.style) { + const rawStyles = {}; + copyRawValuesOnly(rawStyles, props.style, props); + visualProps.style = { ...rawStyles, ...visualProps.style }; + } + return visualProps; +} + +export { useSVGProps }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/build-attrs.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/build-attrs.mjs new file mode 100644 index 00000000..91c98b0d --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/build-attrs.mjs @@ -0,0 +1,57 @@ +import { buildHTMLStyles } from '../../html/utils/build-styles.mjs'; +import { buildSVGPath } from './path.mjs'; + +/** + * Build SVG visual attributes, like cx and style.transform + */ +function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0, +// This is object creation, which we try to avoid per-frame. +...latest }, isSVGTag, transformTemplate, styleProp) { + buildHTMLStyles(state, latest, transformTemplate); + /** + * For svg tags we just want to make sure viewBox is animatable and treat all the styles + * as normal HTML tags. + */ + if (isSVGTag) { + if (state.style.viewBox) { + state.attrs.viewBox = state.style.viewBox; + } + return; + } + state.attrs = state.style; + state.style = {}; + const { attrs, style } = state; + /** + * However, we apply transforms as CSS transforms. + * So if we detect a transform, transformOrigin we take it from attrs and copy it into style. + */ + if (attrs.transform) { + style.transform = attrs.transform; + delete attrs.transform; + } + if (style.transform || attrs.transformOrigin) { + style.transformOrigin = attrs.transformOrigin ?? "50% 50%"; + delete attrs.transformOrigin; + } + if (style.transform) { + /** + * SVG's element transform-origin uses its own median as a reference. + * Therefore, transformBox becomes a fill-box + */ + style.transformBox = styleProp?.transformBox ?? "fill-box"; + delete attrs.transformBox; + } + // Render attrX/attrY/attrScale as attributes + if (attrX !== undefined) + attrs.x = attrX; + if (attrY !== undefined) + attrs.y = attrY; + if (attrScale !== undefined) + attrs.scale = attrScale; + // Build SVG path if one has been defined + if (pathLength !== undefined) { + buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false); + } +} + +export { buildSVGAttrs }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/camel-case-attrs.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/camel-case-attrs.mjs new file mode 100644 index 00000000..4158b535 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/camel-case-attrs.mjs @@ -0,0 +1,30 @@ +/** + * A set of attribute names that are always read/written as camel case. + */ +const camelCaseAttributes = new Set([ + "baseFrequency", + "diffuseConstant", + "kernelMatrix", + "kernelUnitLength", + "keySplines", + "keyTimes", + "limitingConeAngle", + "markerHeight", + "markerWidth", + "numOctaves", + "targetX", + "targetY", + "surfaceScale", + "specularConstant", + "specularExponent", + "stdDeviation", + "tableValues", + "viewBox", + "gradientTransform", + "pathLength", + "startOffset", + "textLength", + "lengthAdjust", +]); + +export { camelCaseAttributes }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/create-render-state.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/create-render-state.mjs new file mode 100644 index 00000000..7eb583e3 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/create-render-state.mjs @@ -0,0 +1,8 @@ +import { createHtmlRenderState } from '../../html/utils/create-render-state.mjs'; + +const createSvgRenderState = () => ({ + ...createHtmlRenderState(), + attrs: {}, +}); + +export { createSvgRenderState }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/is-svg-tag.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/is-svg-tag.mjs new file mode 100644 index 00000000..09178964 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/is-svg-tag.mjs @@ -0,0 +1,3 @@ +const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg"; + +export { isSVGTag }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/path.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/path.mjs new file mode 100644 index 00000000..fc0a43bd --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/path.mjs @@ -0,0 +1,32 @@ +import { px } from 'motion-dom'; + +const dashKeys = { + offset: "stroke-dashoffset", + array: "stroke-dasharray", +}; +const camelKeys = { + offset: "strokeDashoffset", + array: "strokeDasharray", +}; +/** + * Build SVG path properties. Uses the path's measured length to convert + * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset + * and stroke-dasharray attributes. + * + * This function is mutative to reduce per-frame GC. + */ +function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) { + // Normalise path length by setting SVG attribute pathLength to 1 + attrs.pathLength = 1; + // We use dash case when setting attributes directly to the DOM node and camel case + // when defining props on a React component. + const keys = useDashCase ? dashKeys : camelKeys; + // Build the dash offset + attrs[keys.offset] = px.transform(-offset); + // Build the dash array + const pathLength = px.transform(length); + const pathSpacing = px.transform(spacing); + attrs[keys.array] = `${pathLength} ${pathSpacing}`; +} + +export { buildSVGPath }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/render.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/render.mjs new file mode 100644 index 00000000..bfd65f3d --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/render.mjs @@ -0,0 +1,12 @@ +import { camelToDash } from '../../dom/utils/camel-to-dash.mjs'; +import { renderHTML } from '../../html/utils/render.mjs'; +import { camelCaseAttributes } from './camel-case-attrs.mjs'; + +function renderSVG(element, renderState, _styleProp, projection) { + renderHTML(element, renderState, undefined, projection); + for (const key in renderState.attrs) { + element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]); + } +} + +export { renderSVG }; diff --git a/node_modules/framer-motion/dist/es/render/svg/utils/scrape-motion-values.mjs b/node_modules/framer-motion/dist/es/render/svg/utils/scrape-motion-values.mjs new file mode 100644 index 00000000..532dc460 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/svg/utils/scrape-motion-values.mjs @@ -0,0 +1,18 @@ +import { isMotionValue, transformPropOrder } from 'motion-dom'; +import { scrapeMotionValuesFromProps as scrapeMotionValuesFromProps$1 } from '../../html/utils/scrape-motion-values.mjs'; + +function scrapeMotionValuesFromProps(props, prevProps, visualElement) { + const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + for (const key in props) { + if (isMotionValue(props[key]) || + isMotionValue(prevProps[key])) { + const targetKey = transformPropOrder.indexOf(key) !== -1 + ? "attr" + key.charAt(0).toUpperCase() + key.substring(1) + : key; + newValues[targetKey] = props[key]; + } + } + return newValues; +} + +export { scrapeMotionValuesFromProps }; diff --git a/node_modules/framer-motion/dist/es/render/utils/animation-state.mjs b/node_modules/framer-motion/dist/es/render/utils/animation-state.mjs new file mode 100644 index 00000000..794f8a31 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/animation-state.mjs @@ -0,0 +1,342 @@ +import { animateVisualElement } from '../../animation/interfaces/visual-element.mjs'; +import { isAnimationControls } from '../../animation/utils/is-animation-controls.mjs'; +import { isKeyframesTarget } from '../../animation/utils/is-keyframes-target.mjs'; +import { shallowCompare } from '../../utils/shallow-compare.mjs'; +import { getVariantContext } from './get-variant-context.mjs'; +import { isVariantLabel } from './is-variant-label.mjs'; +import { resolveVariant } from './resolve-dynamic-variants.mjs'; +import { variantPriorityOrder } from './variant-props.mjs'; + +const reversePriorityOrder = [...variantPriorityOrder].reverse(); +const numAnimationTypes = variantPriorityOrder.length; +function animateList(visualElement) { + return (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options))); +} +function createAnimationState(visualElement) { + let animate = animateList(visualElement); + let state = createState(); + let isInitialRender = true; + /** + * This function will be used to reduce the animation definitions for + * each active animation type into an object of resolved values for it. + */ + const buildResolvedTypeValues = (type) => (acc, definition) => { + const resolved = resolveVariant(visualElement, definition, type === "exit" + ? visualElement.presenceContext?.custom + : undefined); + if (resolved) { + const { transition, transitionEnd, ...target } = resolved; + acc = { ...acc, ...target, ...transitionEnd }; + } + return acc; + }; + /** + * This just allows us to inject mocked animation functions + * @internal + */ + function setAnimateFunction(makeAnimator) { + animate = makeAnimator(visualElement); + } + /** + * When we receive new props, we need to: + * 1. Create a list of protected keys for each type. This is a directory of + * value keys that are currently being "handled" by types of a higher priority + * so that whenever an animation is played of a given type, these values are + * protected from being animated. + * 2. Determine if an animation type needs animating. + * 3. Determine if any values have been removed from a type and figure out + * what to animate those to. + */ + function animateChanges(changedActiveType) { + const { props } = visualElement; + const context = getVariantContext(visualElement.parent) || {}; + /** + * A list of animations that we'll build into as we iterate through the animation + * types. This will get executed at the end of the function. + */ + const animations = []; + /** + * Keep track of which values have been removed. Then, as we hit lower priority + * animation types, we can check if they contain removed values and animate to that. + */ + const removedKeys = new Set(); + /** + * A dictionary of all encountered keys. This is an object to let us build into and + * copy it without iteration. Each time we hit an animation type we set its protected + * keys - the keys its not allowed to animate - to the latest version of this object. + */ + let encounteredKeys = {}; + /** + * If a variant has been removed at a given index, and this component is controlling + * variant animations, we want to ensure lower-priority variants are forced to animate. + */ + let removedVariantIndex = Infinity; + /** + * Iterate through all animation types in reverse priority order. For each, we want to + * detect which values it's handling and whether or not they've changed (and therefore + * need to be animated). If any values have been removed, we want to detect those in + * lower priority props and flag for animation. + */ + for (let i = 0; i < numAnimationTypes; i++) { + const type = reversePriorityOrder[i]; + const typeState = state[type]; + const prop = props[type] !== undefined + ? props[type] + : context[type]; + const propIsVariant = isVariantLabel(prop); + /** + * If this type has *just* changed isActive status, set activeDelta + * to that status. Otherwise set to null. + */ + const activeDelta = type === changedActiveType ? typeState.isActive : null; + if (activeDelta === false) + removedVariantIndex = i; + /** + * If this prop is an inherited variant, rather than been set directly on the + * component itself, we want to make sure we allow the parent to trigger animations. + * + * TODO: Can probably change this to a !isControllingVariants check + */ + let isInherited = prop === context[type] && + prop !== props[type] && + propIsVariant; + /** + * + */ + if (isInherited && + isInitialRender && + visualElement.manuallyAnimateOnMount) { + isInherited = false; + } + /** + * Set all encountered keys so far as the protected keys for this type. This will + * be any key that has been animated or otherwise handled by active, higher-priortiy types. + */ + typeState.protectedKeys = { ...encounteredKeys }; + // Check if we can skip analysing this prop early + if ( + // If it isn't active and hasn't *just* been set as inactive + (!typeState.isActive && activeDelta === null) || + // If we didn't and don't have any defined prop for this animation type + (!prop && !typeState.prevProp) || + // Or if the prop doesn't define an animation + isAnimationControls(prop) || + typeof prop === "boolean") { + continue; + } + /** + * As we go look through the values defined on this type, if we detect + * a changed value or a value that was removed in a higher priority, we set + * this to true and add this prop to the animation list. + */ + const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop); + let shouldAnimateType = variantDidChange || + // If we're making this variant active, we want to always make it active + (type === changedActiveType && + typeState.isActive && + !isInherited && + propIsVariant) || + // If we removed a higher-priority variant (i is in reverse order) + (i > removedVariantIndex && propIsVariant); + let handledRemovedValues = false; + /** + * As animations can be set as variant lists, variants or target objects, we + * coerce everything to an array if it isn't one already + */ + const definitionList = Array.isArray(prop) ? prop : [prop]; + /** + * Build an object of all the resolved values. We'll use this in the subsequent + * animateChanges calls to determine whether a value has changed. + */ + let resolvedValues = definitionList.reduce(buildResolvedTypeValues(type), {}); + if (activeDelta === false) + resolvedValues = {}; + /** + * Now we need to loop through all the keys in the prev prop and this prop, + * and decide: + * 1. If the value has changed, and needs animating + * 2. If it has been removed, and needs adding to the removedKeys set + * 3. If it has been removed in a higher priority type and needs animating + * 4. If it hasn't been removed in a higher priority but hasn't changed, and + * needs adding to the type's protectedKeys list. + */ + const { prevResolvedValues = {} } = typeState; + const allKeys = { + ...prevResolvedValues, + ...resolvedValues, + }; + const markToAnimate = (key) => { + shouldAnimateType = true; + if (removedKeys.has(key)) { + handledRemovedValues = true; + removedKeys.delete(key); + } + typeState.needsAnimating[key] = true; + const motionValue = visualElement.getValue(key); + if (motionValue) + motionValue.liveStyle = false; + }; + for (const key in allKeys) { + const next = resolvedValues[key]; + const prev = prevResolvedValues[key]; + // If we've already handled this we can just skip ahead + if (encounteredKeys.hasOwnProperty(key)) + continue; + /** + * If the value has changed, we probably want to animate it. + */ + let valueHasChanged = false; + if (isKeyframesTarget(next) && isKeyframesTarget(prev)) { + valueHasChanged = !shallowCompare(next, prev); + } + else { + valueHasChanged = next !== prev; + } + if (valueHasChanged) { + if (next !== undefined && next !== null) { + // If next is defined and doesn't equal prev, it needs animating + markToAnimate(key); + } + else { + // If it's undefined, it's been removed. + removedKeys.add(key); + } + } + else if (next !== undefined && removedKeys.has(key)) { + /** + * If next hasn't changed and it isn't undefined, we want to check if it's + * been removed by a higher priority + */ + markToAnimate(key); + } + else { + /** + * If it hasn't changed, we add it to the list of protected values + * to ensure it doesn't get animated. + */ + typeState.protectedKeys[key] = true; + } + } + /** + * Update the typeState so next time animateChanges is called we can compare the + * latest prop and resolvedValues to these. + */ + typeState.prevProp = prop; + typeState.prevResolvedValues = resolvedValues; + /** + * + */ + if (typeState.isActive) { + encounteredKeys = { ...encounteredKeys, ...resolvedValues }; + } + if (isInitialRender && visualElement.blockInitialAnimation) { + shouldAnimateType = false; + } + /** + * If this is an inherited prop we want to skip this animation + * unless the inherited variants haven't changed on this render. + */ + const willAnimateViaParent = isInherited && variantDidChange; + const needsAnimating = !willAnimateViaParent || handledRemovedValues; + if (shouldAnimateType && needsAnimating) { + animations.push(...definitionList.map((animation) => ({ + animation: animation, + options: { type }, + }))); + } + } + /** + * If there are some removed value that haven't been dealt with, + * we need to create a new animation that falls back either to the value + * defined in the style prop, or the last read value. + */ + if (removedKeys.size) { + const fallbackAnimation = {}; + /** + * If the initial prop contains a transition we can use that, otherwise + * allow the animation function to use the visual element's default. + */ + if (typeof props.initial !== "boolean") { + const initialTransition = resolveVariant(visualElement, Array.isArray(props.initial) + ? props.initial[0] + : props.initial); + if (initialTransition && initialTransition.transition) { + fallbackAnimation.transition = initialTransition.transition; + } + } + removedKeys.forEach((key) => { + const fallbackTarget = visualElement.getBaseTarget(key); + const motionValue = visualElement.getValue(key); + if (motionValue) + motionValue.liveStyle = true; + // @ts-expect-error - @mattgperry to figure if we should do something here + fallbackAnimation[key] = fallbackTarget ?? null; + }); + animations.push({ animation: fallbackAnimation }); + } + let shouldAnimate = Boolean(animations.length); + if (isInitialRender && + (props.initial === false || props.initial === props.animate) && + !visualElement.manuallyAnimateOnMount) { + shouldAnimate = false; + } + isInitialRender = false; + return shouldAnimate ? animate(animations) : Promise.resolve(); + } + /** + * Change whether a certain animation type is active. + */ + function setActive(type, isActive) { + // If the active state hasn't changed, we can safely do nothing here + if (state[type].isActive === isActive) + return Promise.resolve(); + // Propagate active change to children + visualElement.variantChildren?.forEach((child) => child.animationState?.setActive(type, isActive)); + state[type].isActive = isActive; + const animations = animateChanges(type); + for (const key in state) { + state[key].protectedKeys = {}; + } + return animations; + } + return { + animateChanges, + setActive, + setAnimateFunction, + getState: () => state, + reset: () => { + state = createState(); + isInitialRender = true; + }, + }; +} +function checkVariantsDidChange(prev, next) { + if (typeof next === "string") { + return next !== prev; + } + else if (Array.isArray(next)) { + return !shallowCompare(next, prev); + } + return false; +} +function createTypeState(isActive = false) { + return { + isActive, + protectedKeys: {}, + needsAnimating: {}, + prevResolvedValues: {}, + }; +} +function createState() { + return { + animate: createTypeState(true), + whileInView: createTypeState(), + whileHover: createTypeState(), + whileTap: createTypeState(), + whileDrag: createTypeState(), + whileFocus: createTypeState(), + exit: createTypeState(), + }; +} + +export { checkVariantsDidChange, createAnimationState }; diff --git a/node_modules/framer-motion/dist/es/render/utils/compare-by-depth.mjs b/node_modules/framer-motion/dist/es/render/utils/compare-by-depth.mjs new file mode 100644 index 00000000..c34dd745 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/compare-by-depth.mjs @@ -0,0 +1,3 @@ +const compareByDepth = (a, b) => a.depth - b.depth; + +export { compareByDepth }; diff --git a/node_modules/framer-motion/dist/es/render/utils/flat-tree.mjs b/node_modules/framer-motion/dist/es/render/utils/flat-tree.mjs new file mode 100644 index 00000000..75d8f88c --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/flat-tree.mjs @@ -0,0 +1,24 @@ +import { addUniqueItem, removeItem } from 'motion-utils'; +import { compareByDepth } from './compare-by-depth.mjs'; + +class FlatTree { + constructor() { + this.children = []; + this.isDirty = false; + } + add(child) { + addUniqueItem(this.children, child); + this.isDirty = true; + } + remove(child) { + removeItem(this.children, child); + this.isDirty = true; + } + forEach(callback) { + this.isDirty && this.children.sort(compareByDepth); + this.isDirty = false; + this.children.forEach(callback); + } +} + +export { FlatTree }; diff --git a/node_modules/framer-motion/dist/es/render/utils/get-variant-context.mjs b/node_modules/framer-motion/dist/es/render/utils/get-variant-context.mjs new file mode 100644 index 00000000..15e57988 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/get-variant-context.mjs @@ -0,0 +1,28 @@ +import { isVariantLabel } from './is-variant-label.mjs'; +import { variantProps } from './variant-props.mjs'; + +const numVariantProps = variantProps.length; +function getVariantContext(visualElement) { + if (!visualElement) + return undefined; + if (!visualElement.isControllingVariants) { + const context = visualElement.parent + ? getVariantContext(visualElement.parent) || {} + : {}; + if (visualElement.props.initial !== undefined) { + context.initial = visualElement.props.initial; + } + return context; + } + const context = {}; + for (let i = 0; i < numVariantProps; i++) { + const name = variantProps[i]; + const prop = visualElement.props[name]; + if (isVariantLabel(prop) || prop === false) { + context[name] = prop; + } + } + return context; +} + +export { getVariantContext }; diff --git a/node_modules/framer-motion/dist/es/render/utils/is-controlling-variants.mjs b/node_modules/framer-motion/dist/es/render/utils/is-controlling-variants.mjs new file mode 100644 index 00000000..3b24d8b1 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/is-controlling-variants.mjs @@ -0,0 +1,13 @@ +import { isAnimationControls } from '../../animation/utils/is-animation-controls.mjs'; +import { isVariantLabel } from './is-variant-label.mjs'; +import { variantProps } from './variant-props.mjs'; + +function isControllingVariants(props) { + return (isAnimationControls(props.animate) || + variantProps.some((name) => isVariantLabel(props[name]))); +} +function isVariantNode(props) { + return Boolean(isControllingVariants(props) || props.variants); +} + +export { isControllingVariants, isVariantNode }; diff --git a/node_modules/framer-motion/dist/es/render/utils/is-variant-label.mjs b/node_modules/framer-motion/dist/es/render/utils/is-variant-label.mjs new file mode 100644 index 00000000..db4ab5c4 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/is-variant-label.mjs @@ -0,0 +1,8 @@ +/** + * Decides if the supplied variable is variant label + */ +function isVariantLabel(v) { + return typeof v === "string" || Array.isArray(v); +} + +export { isVariantLabel }; diff --git a/node_modules/framer-motion/dist/es/render/utils/motion-values.mjs b/node_modules/framer-motion/dist/es/render/utils/motion-values.mjs new file mode 100644 index 00000000..1db628a3 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/motion-values.mjs @@ -0,0 +1,50 @@ +import { isMotionValue, motionValue } from 'motion-dom'; + +function updateMotionValuesFromProps(element, next, prev) { + for (const key in next) { + const nextValue = next[key]; + const prevValue = prev[key]; + if (isMotionValue(nextValue)) { + /** + * If this is a motion value found in props or style, we want to add it + * to our visual element's motion value map. + */ + element.addValue(key, nextValue); + } + else if (isMotionValue(prevValue)) { + /** + * If we're swapping from a motion value to a static value, + * create a new motion value from that + */ + element.addValue(key, motionValue(nextValue, { owner: element })); + } + else if (prevValue !== nextValue) { + /** + * If this is a flat value that has changed, update the motion value + * or create one if it doesn't exist. We only want to do this if we're + * not handling the value with our animation state. + */ + if (element.hasValue(key)) { + const existingValue = element.getValue(key); + if (existingValue.liveStyle === true) { + existingValue.jump(nextValue); + } + else if (!existingValue.hasAnimated) { + existingValue.set(nextValue); + } + } + else { + const latestValue = element.getStaticValue(key); + element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element })); + } + } + } + // Handle removed values + for (const key in prev) { + if (next[key] === undefined) + element.removeValue(key); + } + return next; +} + +export { updateMotionValuesFromProps }; diff --git a/node_modules/framer-motion/dist/es/render/utils/resolve-dynamic-variants.mjs b/node_modules/framer-motion/dist/es/render/utils/resolve-dynamic-variants.mjs new file mode 100644 index 00000000..57b1cea5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/resolve-dynamic-variants.mjs @@ -0,0 +1,8 @@ +import { resolveVariantFromProps } from './resolve-variants.mjs'; + +function resolveVariant(visualElement, definition, custom) { + const props = visualElement.getProps(); + return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement); +} + +export { resolveVariant }; diff --git a/node_modules/framer-motion/dist/es/render/utils/resolve-variants.mjs b/node_modules/framer-motion/dist/es/render/utils/resolve-variants.mjs new file mode 100644 index 00000000..ba65fc29 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/resolve-variants.mjs @@ -0,0 +1,36 @@ +function getValueState(visualElement) { + const state = [{}, {}]; + visualElement?.values.forEach((value, key) => { + state[0][key] = value.get(); + state[1][key] = value.getVelocity(); + }); + return state; +} +function resolveVariantFromProps(props, definition, custom, visualElement) { + /** + * If the variant definition is a function, resolve. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + /** + * If the variant definition is a variant label, or + * the function returned a variant label, resolve. + */ + if (typeof definition === "string") { + definition = props.variants && props.variants[definition]; + } + /** + * At this point we've resolved both functions and variant labels, + * but the resolved variant label might itself have been a function. + * If so, resolve. This can only have returned a valid target object. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + return definition; +} + +export { resolveVariantFromProps }; diff --git a/node_modules/framer-motion/dist/es/render/utils/setters.mjs b/node_modules/framer-motion/dist/es/render/utils/setters.mjs new file mode 100644 index 00000000..6ea78049 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/setters.mjs @@ -0,0 +1,31 @@ +import { motionValue } from 'motion-dom'; +import { isKeyframesTarget } from '../../animation/utils/is-keyframes-target.mjs'; +import { resolveVariant } from './resolve-dynamic-variants.mjs'; + +/** + * Set VisualElement's MotionValue, creating a new MotionValue for it if + * it doesn't exist. + */ +function setMotionValue(visualElement, key, value) { + if (visualElement.hasValue(key)) { + visualElement.getValue(key).set(value); + } + else { + visualElement.addValue(key, motionValue(value)); + } +} +function resolveFinalValueInKeyframes(v) { + // TODO maybe throw if v.length - 1 is placeholder token? + return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; +} +function setTarget(visualElement, definition) { + const resolved = resolveVariant(visualElement, definition); + let { transitionEnd = {}, transition = {}, ...target } = resolved || {}; + target = { ...target, ...transitionEnd }; + for (const key in target) { + const value = resolveFinalValueInKeyframes(target[key]); + setMotionValue(visualElement, key, value); + } +} + +export { setTarget }; diff --git a/node_modules/framer-motion/dist/es/render/utils/variant-props.mjs b/node_modules/framer-motion/dist/es/render/utils/variant-props.mjs new file mode 100644 index 00000000..f4d0a911 --- /dev/null +++ b/node_modules/framer-motion/dist/es/render/utils/variant-props.mjs @@ -0,0 +1,12 @@ +const variantPriorityOrder = [ + "animate", + "whileInView", + "whileFocus", + "whileHover", + "whileTap", + "whileDrag", + "exit", +]; +const variantProps = ["initial", ...variantPriorityOrder]; + +export { variantPriorityOrder, variantProps }; diff --git a/node_modules/framer-motion/dist/es/utils/delay.mjs b/node_modules/framer-motion/dist/es/utils/delay.mjs new file mode 100644 index 00000000..6dceb872 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/delay.mjs @@ -0,0 +1,23 @@ +import { time, frame, cancelFrame } from 'motion-dom'; +import { secondsToMilliseconds } from 'motion-utils'; + +/** + * Timeout defined in ms + */ +function delay(callback, timeout) { + const start = time.now(); + const checkElapsed = ({ timestamp }) => { + const elapsed = timestamp - start; + if (elapsed >= timeout) { + cancelFrame(checkElapsed); + callback(elapsed - timeout); + } + }; + frame.setup(checkElapsed, true); + return () => cancelFrame(checkElapsed); +} +function delayInSeconds(callback, timeout) { + return delay(callback, secondsToMilliseconds(timeout)); +} + +export { delay, delayInSeconds }; diff --git a/node_modules/framer-motion/dist/es/utils/distance.mjs b/node_modules/framer-motion/dist/es/utils/distance.mjs new file mode 100644 index 00000000..dfcc1077 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/distance.mjs @@ -0,0 +1,9 @@ +const distance = (a, b) => Math.abs(a - b); +function distance2D(a, b) { + // Multi-dimensional + const xDelta = distance(a.x, b.x); + const yDelta = distance(a.y, b.y); + return Math.sqrt(xDelta ** 2 + yDelta ** 2); +} + +export { distance, distance2D }; diff --git a/node_modules/framer-motion/dist/es/utils/get-context-window.mjs b/node_modules/framer-motion/dist/es/utils/get-context-window.mjs new file mode 100644 index 00000000..5e389050 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/get-context-window.mjs @@ -0,0 +1,6 @@ +// Fixes https://github.com/motiondivision/motion/issues/2270 +const getContextWindow = ({ current }) => { + return current ? current.ownerDocument.defaultView : null; +}; + +export { getContextWindow }; diff --git a/node_modules/framer-motion/dist/es/utils/is-browser.mjs b/node_modules/framer-motion/dist/es/utils/is-browser.mjs new file mode 100644 index 00000000..bd45170d --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/is-browser.mjs @@ -0,0 +1,3 @@ +const isBrowser = typeof window !== "undefined"; + +export { isBrowser }; diff --git a/node_modules/framer-motion/dist/es/utils/is-ref-object.mjs b/node_modules/framer-motion/dist/es/utils/is-ref-object.mjs new file mode 100644 index 00000000..1244527f --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/is-ref-object.mjs @@ -0,0 +1,7 @@ +function isRefObject(ref) { + return (ref && + typeof ref === "object" && + Object.prototype.hasOwnProperty.call(ref, "current")); +} + +export { isRefObject }; diff --git a/node_modules/framer-motion/dist/es/utils/reduced-motion/index.mjs b/node_modules/framer-motion/dist/es/utils/reduced-motion/index.mjs new file mode 100644 index 00000000..a2d76e8f --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/reduced-motion/index.mjs @@ -0,0 +1,19 @@ +import { isBrowser } from '../is-browser.mjs'; +import { hasReducedMotionListener, prefersReducedMotion } from './state.mjs'; + +function initPrefersReducedMotion() { + hasReducedMotionListener.current = true; + if (!isBrowser) + return; + if (window.matchMedia) { + const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)"); + const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches); + motionMediaQuery.addEventListener("change", setReducedMotionPreferences); + setReducedMotionPreferences(); + } + else { + prefersReducedMotion.current = false; + } +} + +export { initPrefersReducedMotion }; diff --git a/node_modules/framer-motion/dist/es/utils/reduced-motion/state.mjs b/node_modules/framer-motion/dist/es/utils/reduced-motion/state.mjs new file mode 100644 index 00000000..6cea5570 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/reduced-motion/state.mjs @@ -0,0 +1,5 @@ +// Does this device prefer reduced motion? Returns `null` server-side. +const prefersReducedMotion = { current: null }; +const hasReducedMotionListener = { current: false }; + +export { hasReducedMotionListener, prefersReducedMotion }; diff --git a/node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion-config.mjs b/node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion-config.mjs new file mode 100644 index 00000000..bec54ebe --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion-config.mjs @@ -0,0 +1,19 @@ +import { useContext } from 'react'; +import { MotionConfigContext } from '../../context/MotionConfigContext.mjs'; +import { useReducedMotion } from './use-reduced-motion.mjs'; + +function useReducedMotionConfig() { + const reducedMotionPreference = useReducedMotion(); + const { reducedMotion } = useContext(MotionConfigContext); + if (reducedMotion === "never") { + return false; + } + else if (reducedMotion === "always") { + return true; + } + else { + return reducedMotionPreference; + } +} + +export { useReducedMotionConfig }; diff --git a/node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.mjs b/node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.mjs new file mode 100644 index 00000000..51a22ac5 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.mjs @@ -0,0 +1,47 @@ +import { warnOnce } from 'motion-utils'; +import { useState } from 'react'; +import { initPrefersReducedMotion } from './index.mjs'; +import { hasReducedMotionListener, prefersReducedMotion } from './state.mjs'; + +/** + * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting. + * + * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing + * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. + * + * It will actively respond to changes and re-render your components with the latest setting. + * + * ```jsx + * export function Sidebar({ isOpen }) { + * const shouldReduceMotion = useReducedMotion() + * const closedX = shouldReduceMotion ? 0 : "-100%" + * + * return ( + * + * ) + * } + * ``` + * + * @return boolean + * + * @public + */ +function useReducedMotion() { + /** + * Lazy initialisation of prefersReducedMotion + */ + !hasReducedMotionListener.current && initPrefersReducedMotion(); + const [shouldReduceMotion] = useState(prefersReducedMotion.current); + if (process.env.NODE_ENV !== "production") { + warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + /** + * TODO See if people miss automatically updating shouldReduceMotion setting + */ + return shouldReduceMotion; +} + +export { useReducedMotion }; diff --git a/node_modules/framer-motion/dist/es/utils/shallow-compare.mjs b/node_modules/framer-motion/dist/es/utils/shallow-compare.mjs new file mode 100644 index 00000000..6b9861aa --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/shallow-compare.mjs @@ -0,0 +1,14 @@ +function shallowCompare(next, prev) { + if (!Array.isArray(prev)) + return false; + const prevLength = prev.length; + if (prevLength !== next.length) + return false; + for (let i = 0; i < prevLength; i++) { + if (prev[i] !== next[i]) + return false; + } + return true; +} + +export { shallowCompare }; diff --git a/node_modules/framer-motion/dist/es/utils/use-animation-frame.mjs b/node_modules/framer-motion/dist/es/utils/use-animation-frame.mjs new file mode 100644 index 00000000..72e895fc --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-animation-frame.mjs @@ -0,0 +1,21 @@ +import { frame, cancelFrame } from 'motion-dom'; +import { useRef, useContext, useEffect } from 'react'; +import { MotionConfigContext } from '../context/MotionConfigContext.mjs'; + +function useAnimationFrame(callback) { + const initialTimestamp = useRef(0); + const { isStatic } = useContext(MotionConfigContext); + useEffect(() => { + if (isStatic) + return; + const provideTimeSinceStart = ({ timestamp, delta }) => { + if (!initialTimestamp.current) + initialTimestamp.current = timestamp; + callback(timestamp - initialTimestamp.current, delta); + }; + frame.update(provideTimeSinceStart, true); + return () => cancelFrame(provideTimeSinceStart); + }, [callback]); +} + +export { useAnimationFrame }; diff --git a/node_modules/framer-motion/dist/es/utils/use-constant.mjs b/node_modules/framer-motion/dist/es/utils/use-constant.mjs new file mode 100644 index 00000000..7b271abc --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-constant.mjs @@ -0,0 +1,18 @@ +import { useRef } from 'react'; + +/** + * Creates a constant value over the lifecycle of a component. + * + * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer + * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` + * you can ensure that initialisers don't execute twice or more. + */ +function useConstant(init) { + const ref = useRef(null); + if (ref.current === null) { + ref.current = init(); + } + return ref.current; +} + +export { useConstant }; diff --git a/node_modules/framer-motion/dist/es/utils/use-cycle.mjs b/node_modules/framer-motion/dist/es/utils/use-cycle.mjs new file mode 100644 index 00000000..4cca7d29 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-cycle.mjs @@ -0,0 +1,47 @@ +import { wrap } from 'motion-utils'; +import { useRef, useState, useCallback } from 'react'; + +/** + * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments. + * + * An index value can be passed to the returned `cycle` function to cycle to a specific index. + * + * ```jsx + * import * as React from "react" + * import { motion, useCycle } from "framer-motion" + * + * export const MyComponent = () => { + * const [x, cycleX] = useCycle(0, 50, 100) + * + * return ( + * cycleX()} + * /> + * ) + * } + * ``` + * + * @param items - items to cycle through + * @returns [currentState, cycleState] + * + * @public + */ +function useCycle(...items) { + const index = useRef(0); + const [item, setItem] = useState(items[index.current]); + const runCycle = useCallback((next) => { + index.current = + typeof next !== "number" + ? wrap(0, items.length, index.current + 1) + : next; + setItem(items[index.current]); + }, + // The array will change on each call, but by putting items.length at + // the front of this array, we guarantee the dependency comparison will match up + // eslint-disable-next-line react-hooks/exhaustive-deps + [items.length, ...items]); + return [item, runCycle]; +} + +export { useCycle }; diff --git a/node_modules/framer-motion/dist/es/utils/use-force-update.mjs b/node_modules/framer-motion/dist/es/utils/use-force-update.mjs new file mode 100644 index 00000000..7b968b9f --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-force-update.mjs @@ -0,0 +1,19 @@ +import { frame } from 'motion-dom'; +import { useState, useCallback } from 'react'; +import { useIsMounted } from './use-is-mounted.mjs'; + +function useForceUpdate() { + const isMounted = useIsMounted(); + const [forcedRenderCount, setForcedRenderCount] = useState(0); + const forceRender = useCallback(() => { + isMounted.current && setForcedRenderCount(forcedRenderCount + 1); + }, [forcedRenderCount]); + /** + * Defer this to the end of the next animation frame in case there are multiple + * synchronous calls. + */ + const deferredForceRender = useCallback(() => frame.postRender(forceRender), [forceRender]); + return [deferredForceRender, forcedRenderCount]; +} + +export { useForceUpdate }; diff --git a/node_modules/framer-motion/dist/es/utils/use-in-view.mjs b/node_modules/framer-motion/dist/es/utils/use-in-view.mjs new file mode 100644 index 00000000..3361429a --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-in-view.mjs @@ -0,0 +1,23 @@ +import { useState, useEffect } from 'react'; +import { inView } from '../render/dom/viewport/index.mjs'; + +function useInView(ref, { root, margin, amount, once = false, initial = false, } = {}) { + const [isInView, setInView] = useState(initial); + useEffect(() => { + if (!ref.current || (once && isInView)) + return; + const onEnter = () => { + setInView(true); + return once ? undefined : () => setInView(false); + }; + const options = { + root: (root && root.current) || undefined, + margin, + amount, + }; + return inView(ref.current, onEnter, options); + }, [root, ref, margin, once, amount]); + return isInView; +} + +export { useInView }; diff --git a/node_modules/framer-motion/dist/es/utils/use-instant-transition.mjs b/node_modules/framer-motion/dist/es/utils/use-instant-transition.mjs new file mode 100644 index 00000000..b9d54157 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-instant-transition.mjs @@ -0,0 +1,41 @@ +import { frame } from 'motion-dom'; +import { MotionGlobalConfig } from 'motion-utils'; +import { useRef, useEffect } from 'react'; +import { useInstantLayoutTransition } from '../projection/use-instant-layout-transition.mjs'; +import { useForceUpdate } from './use-force-update.mjs'; + +function useInstantTransition() { + const [forceUpdate, forcedRenderCount] = useForceUpdate(); + const startInstantLayoutTransition = useInstantLayoutTransition(); + const unlockOnFrameRef = useRef(-1); + useEffect(() => { + /** + * Unblock after two animation frames, otherwise this will unblock too soon. + */ + frame.postRender(() => frame.postRender(() => { + /** + * If the callback has been called again after the effect + * triggered this 2 frame delay, don't unblock animations. This + * prevents the previous effect from unblocking the current + * instant transition too soon. This becomes more likely when + * used in conjunction with React.startTransition(). + */ + if (forcedRenderCount !== unlockOnFrameRef.current) + return; + MotionGlobalConfig.instantAnimations = false; + })); + }, [forcedRenderCount]); + return (callback) => { + startInstantLayoutTransition(() => { + MotionGlobalConfig.instantAnimations = true; + forceUpdate(); + callback(); + unlockOnFrameRef.current = forcedRenderCount + 1; + }); + }; +} +function disableInstantTransitions() { + MotionGlobalConfig.instantAnimations = false; +} + +export { disableInstantTransitions, useInstantTransition }; diff --git a/node_modules/framer-motion/dist/es/utils/use-is-mounted.mjs b/node_modules/framer-motion/dist/es/utils/use-is-mounted.mjs new file mode 100644 index 00000000..63528baf --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-is-mounted.mjs @@ -0,0 +1,15 @@ +import { useRef } from 'react'; +import { useIsomorphicLayoutEffect } from './use-isomorphic-effect.mjs'; + +function useIsMounted() { + const isMounted = useRef(false); + useIsomorphicLayoutEffect(() => { + isMounted.current = true; + return () => { + isMounted.current = false; + }; + }, []); + return isMounted; +} + +export { useIsMounted }; diff --git a/node_modules/framer-motion/dist/es/utils/use-isomorphic-effect.mjs b/node_modules/framer-motion/dist/es/utils/use-isomorphic-effect.mjs new file mode 100644 index 00000000..024536c7 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-isomorphic-effect.mjs @@ -0,0 +1,6 @@ +import { useLayoutEffect, useEffect } from 'react'; +import { isBrowser } from './is-browser.mjs'; + +const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect; + +export { useIsomorphicLayoutEffect }; diff --git a/node_modules/framer-motion/dist/es/utils/use-motion-value-event.mjs b/node_modules/framer-motion/dist/es/utils/use-motion-value-event.mjs new file mode 100644 index 00000000..d4488b64 --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-motion-value-event.mjs @@ -0,0 +1,13 @@ +import { useInsertionEffect } from 'react'; + +function useMotionValueEvent(value, event, callback) { + /** + * useInsertionEffect will create subscriptions before any other + * effects will run. Effects run upwards through the tree so it + * can be that binding a useLayoutEffect higher up the tree can + * miss changes from lower down the tree. + */ + useInsertionEffect(() => value.on(event, callback), [value, event, callback]); +} + +export { useMotionValueEvent }; diff --git a/node_modules/framer-motion/dist/es/utils/use-page-in-view.mjs b/node_modules/framer-motion/dist/es/utils/use-page-in-view.mjs new file mode 100644 index 00000000..aa55bbee --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-page-in-view.mjs @@ -0,0 +1,18 @@ +import { useState, useEffect } from 'react'; + +function usePageInView() { + const [isInView, setIsInView] = useState(true); + useEffect(() => { + const handleVisibilityChange = () => setIsInView(!document.hidden); + if (document.hidden) { + handleVisibilityChange(); + } + document.addEventListener("visibilitychange", handleVisibilityChange); + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, []); + return isInView; +} + +export { usePageInView }; diff --git a/node_modules/framer-motion/dist/es/utils/use-unmount-effect.mjs b/node_modules/framer-motion/dist/es/utils/use-unmount-effect.mjs new file mode 100644 index 00000000..6c955cfb --- /dev/null +++ b/node_modules/framer-motion/dist/es/utils/use-unmount-effect.mjs @@ -0,0 +1,7 @@ +import { useEffect } from 'react'; + +function useUnmountEffect(callback) { + return useEffect(() => () => callback(), []); +} + +export { useUnmountEffect }; diff --git a/node_modules/framer-motion/dist/es/value/scroll/use-element-scroll.mjs b/node_modules/framer-motion/dist/es/value/scroll/use-element-scroll.mjs new file mode 100644 index 00000000..83f57324 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/scroll/use-element-scroll.mjs @@ -0,0 +1,14 @@ +import { warnOnce } from 'motion-utils'; +import { useScroll } from '../use-scroll.mjs'; + +/** + * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref }) + */ +function useElementScroll(ref) { + if (process.env.NODE_ENV === "development") { + warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref })."); + } + return useScroll({ container: ref }); +} + +export { useElementScroll }; diff --git a/node_modules/framer-motion/dist/es/value/scroll/use-viewport-scroll.mjs b/node_modules/framer-motion/dist/es/value/scroll/use-viewport-scroll.mjs new file mode 100644 index 00000000..84be0f10 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/scroll/use-viewport-scroll.mjs @@ -0,0 +1,14 @@ +import { warnOnce } from 'motion-utils'; +import { useScroll } from '../use-scroll.mjs'; + +/** + * @deprecated useViewportScroll is deprecated. Convert to useScroll() + */ +function useViewportScroll() { + if (process.env.NODE_ENV !== "production") { + warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll()."); + } + return useScroll(); +} + +export { useViewportScroll }; diff --git a/node_modules/framer-motion/dist/es/value/use-combine-values.mjs b/node_modules/framer-motion/dist/es/value/use-combine-values.mjs new file mode 100644 index 00000000..b79d3b86 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-combine-values.mjs @@ -0,0 +1,37 @@ +import { cancelFrame, frame } from 'motion-dom'; +import { useIsomorphicLayoutEffect } from '../utils/use-isomorphic-effect.mjs'; +import { useMotionValue } from './use-motion-value.mjs'; + +function useCombineMotionValues(values, combineValues) { + /** + * Initialise the returned motion value. This remains the same between renders. + */ + const value = useMotionValue(combineValues()); + /** + * Create a function that will update the template motion value with the latest values. + * This is pre-bound so whenever a motion value updates it can schedule its + * execution in Framesync. If it's already been scheduled it won't be fired twice + * in a single frame. + */ + const updateValue = () => value.set(combineValues()); + /** + * Synchronously update the motion value with the latest values during the render. + * This ensures that within a React render, the styles applied to the DOM are up-to-date. + */ + updateValue(); + /** + * Subscribe to all motion values found within the template. Whenever any of them change, + * schedule an update. + */ + useIsomorphicLayoutEffect(() => { + const scheduleUpdate = () => frame.preRender(updateValue, false, true); + const subscriptions = values.map((v) => v.on("change", scheduleUpdate)); + return () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + cancelFrame(updateValue); + }; + }); + return value; +} + +export { useCombineMotionValues }; diff --git a/node_modules/framer-motion/dist/es/value/use-computed.mjs b/node_modules/framer-motion/dist/es/value/use-computed.mjs new file mode 100644 index 00000000..70827c97 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-computed.mjs @@ -0,0 +1,19 @@ +import { collectMotionValues } from 'motion-dom'; +import { useCombineMotionValues } from './use-combine-values.mjs'; + +function useComputed(compute) { + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * will be saved into this array. + */ + collectMotionValues.current = []; + compute(); + const value = useCombineMotionValues(collectMotionValues.current, compute); + /** + * Synchronously close session of collectMotionValues. + */ + collectMotionValues.current = undefined; + return value; +} + +export { useComputed }; diff --git a/node_modules/framer-motion/dist/es/value/use-inverted-scale.mjs b/node_modules/framer-motion/dist/es/value/use-inverted-scale.mjs new file mode 100644 index 00000000..31f15ee6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-inverted-scale.mjs @@ -0,0 +1,52 @@ +import { invariant, warning } from 'motion-utils'; +import { useContext } from 'react'; +import { MotionContext } from '../context/MotionContext/index.mjs'; +import { useMotionValue } from './use-motion-value.mjs'; +import { useTransform } from './use-transform.mjs'; + +// Keep things reasonable and avoid scale: Infinity. In practise we might need +// to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1] +// to simply hide content at unreasonable scales. +const maxScale = 100000; +const invertScale = (scale) => scale > 0.001 ? 1 / scale : maxScale; +let hasWarned = false; +/** + * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse + * of their respective parent scales. + * + * This is useful for undoing the distortion of content when scaling a parent component. + * + * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent. + * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output + * of those instead. + * + * ```jsx + * const MyComponent = () => { + * const { scaleX, scaleY } = useInvertedScale() + * return + * } + * ``` + * + * @deprecated + */ +function useInvertedScale(scale) { + let parentScaleX = useMotionValue(1); + let parentScaleY = useMotionValue(1); + const { visualElement } = useContext(MotionContext); + invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component."); + warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead."); + hasWarned = true; + if (scale) { + parentScaleX = scale.scaleX || parentScaleX; + parentScaleY = scale.scaleY || parentScaleY; + } + else if (visualElement) { + parentScaleX = visualElement.getValue("scaleX", 1); + parentScaleY = visualElement.getValue("scaleY", 1); + } + const scaleX = useTransform(parentScaleX, invertScale); + const scaleY = useTransform(parentScaleY, invertScale); + return { scaleX, scaleY }; +} + +export { invertScale, useInvertedScale }; diff --git a/node_modules/framer-motion/dist/es/value/use-motion-template.mjs b/node_modules/framer-motion/dist/es/value/use-motion-template.mjs new file mode 100644 index 00000000..3c41024a --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-motion-template.mjs @@ -0,0 +1,45 @@ +import { isMotionValue } from 'motion-dom'; +import { useCombineMotionValues } from './use-combine-values.mjs'; + +/** + * Combine multiple motion values into a new one using a string template literal. + * + * ```jsx + * import { + * motion, + * useSpring, + * useMotionValue, + * useMotionTemplate + * } from "framer-motion" + * + * function Component() { + * const shadowX = useSpring(0) + * const shadowY = useMotionValue(0) + * const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))` + * + * return + * } + * ``` + * + * @public + */ +function useMotionTemplate(fragments, ...values) { + /** + * Create a function that will build a string from the latest motion values. + */ + const numFragments = fragments.length; + function buildValue() { + let output = ``; + for (let i = 0; i < numFragments; i++) { + output += fragments[i]; + const value = values[i]; + if (value) { + output += isMotionValue(value) ? value.get() : value; + } + } + return output; + } + return useCombineMotionValues(values.filter(isMotionValue), buildValue); +} + +export { useMotionTemplate }; diff --git a/node_modules/framer-motion/dist/es/value/use-motion-value.mjs b/node_modules/framer-motion/dist/es/value/use-motion-value.mjs new file mode 100644 index 00000000..3a8a6627 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-motion-value.mjs @@ -0,0 +1,38 @@ +import { motionValue } from 'motion-dom'; +import { useContext, useState, useEffect } from 'react'; +import { MotionConfigContext } from '../context/MotionConfigContext.mjs'; +import { useConstant } from '../utils/use-constant.mjs'; + +/** + * Creates a `MotionValue` to track the state and velocity of a value. + * + * Usually, these are created automatically. For advanced use-cases, like use with `useTransform`, you can create `MotionValue`s externally and pass them into the animated component via the `style` prop. + * + * ```jsx + * export const MyComponent = () => { + * const scale = useMotionValue(1) + * + * return + * } + * ``` + * + * @param initial - The initial state. + * + * @public + */ +function useMotionValue(initial) { + const value = useConstant(() => motionValue(initial)); + /** + * If this motion value is being used in static mode, like on + * the Framer canvas, force components to rerender when the motion + * value is updated. + */ + const { isStatic } = useContext(MotionConfigContext); + if (isStatic) { + const [, setLatest] = useState(initial); + useEffect(() => value.on("change", setLatest), []); + } + return value; +} + +export { useMotionValue }; diff --git a/node_modules/framer-motion/dist/es/value/use-scroll.mjs b/node_modules/framer-motion/dist/es/value/use-scroll.mjs new file mode 100644 index 00000000..85703ea9 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-scroll.mjs @@ -0,0 +1,39 @@ +import { motionValue } from 'motion-dom'; +import { warning } from 'motion-utils'; +import { useEffect } from 'react'; +import { scroll } from '../render/dom/scroll/index.mjs'; +import { useConstant } from '../utils/use-constant.mjs'; +import { useIsomorphicLayoutEffect } from '../utils/use-isomorphic-effect.mjs'; + +function refWarning(name, ref) { + warning(Boolean(!ref || ref.current), `You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`); +} +const createScrollMotionValues = () => ({ + scrollX: motionValue(0), + scrollY: motionValue(0), + scrollXProgress: motionValue(0), + scrollYProgress: motionValue(0), +}); +function useScroll({ container, target, layoutEffect = true, ...options } = {}) { + const values = useConstant(createScrollMotionValues); + const useLifecycleEffect = layoutEffect + ? useIsomorphicLayoutEffect + : useEffect; + useLifecycleEffect(() => { + refWarning("target", target); + refWarning("container", container); + return scroll((_progress, { x, y, }) => { + values.scrollX.set(x.current); + values.scrollXProgress.set(x.progress); + values.scrollY.set(y.current); + values.scrollYProgress.set(y.progress); + }, { + ...options, + container: container?.current || undefined, + target: target?.current || undefined, + }); + }, [container, target, JSON.stringify(options.offset)]); + return values; +} + +export { useScroll }; diff --git a/node_modules/framer-motion/dist/es/value/use-spring.mjs b/node_modules/framer-motion/dist/es/value/use-spring.mjs new file mode 100644 index 00000000..1daac6e2 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-spring.mjs @@ -0,0 +1,21 @@ +import { attachSpring, isMotionValue } from 'motion-dom'; +import { useContext, useInsertionEffect } from 'react'; +import { MotionConfigContext } from '../context/MotionConfigContext.mjs'; +import { useMotionValue } from './use-motion-value.mjs'; +import { useTransform } from './use-transform.mjs'; + +function useSpring(source, options = {}) { + const { isStatic } = useContext(MotionConfigContext); + const getFromSource = () => (isMotionValue(source) ? source.get() : source); + // isStatic will never change, allowing early hooks return + if (isStatic) { + return useTransform(getFromSource); + } + const value = useMotionValue(getFromSource()); + useInsertionEffect(() => { + return attachSpring(value, source, options); + }, [value, JSON.stringify(options)]); + return value; +} + +export { useSpring }; diff --git a/node_modules/framer-motion/dist/es/value/use-time.mjs b/node_modules/framer-motion/dist/es/value/use-time.mjs new file mode 100644 index 00000000..390a5e46 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-time.mjs @@ -0,0 +1,10 @@ +import { useAnimationFrame } from '../utils/use-animation-frame.mjs'; +import { useMotionValue } from './use-motion-value.mjs'; + +function useTime() { + const time = useMotionValue(0); + useAnimationFrame((t) => time.set(t)); + return time; +} + +export { useTime }; diff --git a/node_modules/framer-motion/dist/es/value/use-transform.mjs b/node_modules/framer-motion/dist/es/value/use-transform.mjs new file mode 100644 index 00000000..eef2c2fe --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-transform.mjs @@ -0,0 +1,29 @@ +import { transform } from 'motion-dom'; +import { useConstant } from '../utils/use-constant.mjs'; +import { useCombineMotionValues } from './use-combine-values.mjs'; +import { useComputed } from './use-computed.mjs'; + +function useTransform(input, inputRangeOrTransformer, outputRange, options) { + if (typeof input === "function") { + return useComputed(input); + } + const transformer = typeof inputRangeOrTransformer === "function" + ? inputRangeOrTransformer + : transform(inputRangeOrTransformer, outputRange, options); + return Array.isArray(input) + ? useListTransform(input, transformer) + : useListTransform([input], ([latest]) => transformer(latest)); +} +function useListTransform(values, transformer) { + const latest = useConstant(() => []); + return useCombineMotionValues(values, () => { + latest.length = 0; + const numValues = values.length; + for (let i = 0; i < numValues; i++) { + latest[i] = values[i].get(); + } + return transformer(latest); + }); +} + +export { useTransform }; diff --git a/node_modules/framer-motion/dist/es/value/use-velocity.mjs b/node_modules/framer-motion/dist/es/value/use-velocity.mjs new file mode 100644 index 00000000..fd665f56 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-velocity.mjs @@ -0,0 +1,35 @@ +import { frame } from 'motion-dom'; +import { useMotionValueEvent } from '../utils/use-motion-value-event.mjs'; +import { useMotionValue } from './use-motion-value.mjs'; + +/** + * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes. + * + * ```javascript + * const x = useMotionValue(0) + * const xVelocity = useVelocity(x) + * const xAcceleration = useVelocity(xVelocity) + * ``` + * + * @public + */ +function useVelocity(value) { + const velocity = useMotionValue(value.getVelocity()); + const updateVelocity = () => { + const latest = value.getVelocity(); + velocity.set(latest); + /** + * If we still have velocity, schedule an update for the next frame + * to keep checking until it is zero. + */ + if (latest) + frame.update(updateVelocity); + }; + useMotionValueEvent(value, "change", () => { + // Schedule an update to this value at the end of the current frame. + frame.update(updateVelocity, false, true); + }); + return velocity; +} + +export { useVelocity }; diff --git a/node_modules/framer-motion/dist/es/value/use-will-change/WillChangeMotionValue.mjs b/node_modules/framer-motion/dist/es/value/use-will-change/WillChangeMotionValue.mjs new file mode 100644 index 00000000..3e49bbd3 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-will-change/WillChangeMotionValue.mjs @@ -0,0 +1,19 @@ +import { MotionValue, transformProps, acceleratedValues } from 'motion-dom'; + +class WillChangeMotionValue extends MotionValue { + constructor() { + super(...arguments); + this.isEnabled = false; + } + add(name) { + if (transformProps.has(name) || acceleratedValues.has(name)) { + this.isEnabled = true; + this.update(); + } + } + update() { + this.set(this.isEnabled ? "transform" : "auto"); + } +} + +export { WillChangeMotionValue }; diff --git a/node_modules/framer-motion/dist/es/value/use-will-change/add-will-change.mjs b/node_modules/framer-motion/dist/es/value/use-will-change/add-will-change.mjs new file mode 100644 index 00000000..001d9701 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-will-change/add-will-change.mjs @@ -0,0 +1,20 @@ +import { MotionGlobalConfig } from 'motion-utils'; +import { isWillChangeMotionValue } from './is.mjs'; + +function addValueToWillChange(visualElement, key) { + const willChange = visualElement.getValue("willChange"); + /** + * It could be that a user has set willChange to a regular MotionValue, + * in which case we can't add the value to it. + */ + if (isWillChangeMotionValue(willChange)) { + return willChange.add(key); + } + else if (!willChange && MotionGlobalConfig.WillChange) { + const newWillChange = new MotionGlobalConfig.WillChange("auto"); + visualElement.addValue("willChange", newWillChange); + newWillChange.add(key); + } +} + +export { addValueToWillChange }; diff --git a/node_modules/framer-motion/dist/es/value/use-will-change/index.mjs b/node_modules/framer-motion/dist/es/value/use-will-change/index.mjs new file mode 100644 index 00000000..356a94d6 --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-will-change/index.mjs @@ -0,0 +1,8 @@ +import { useConstant } from '../../utils/use-constant.mjs'; +import { WillChangeMotionValue } from './WillChangeMotionValue.mjs'; + +function useWillChange() { + return useConstant(() => new WillChangeMotionValue("auto")); +} + +export { useWillChange }; diff --git a/node_modules/framer-motion/dist/es/value/use-will-change/is.mjs b/node_modules/framer-motion/dist/es/value/use-will-change/is.mjs new file mode 100644 index 00000000..a5502f9a --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/use-will-change/is.mjs @@ -0,0 +1,7 @@ +import { isMotionValue } from 'motion-dom'; + +function isWillChangeMotionValue(value) { + return Boolean(isMotionValue(value) && value.add); +} + +export { isWillChangeMotionValue }; diff --git a/node_modules/framer-motion/dist/es/value/utils/resolve-motion-value.mjs b/node_modules/framer-motion/dist/es/value/utils/resolve-motion-value.mjs new file mode 100644 index 00000000..8cdd4b2e --- /dev/null +++ b/node_modules/framer-motion/dist/es/value/utils/resolve-motion-value.mjs @@ -0,0 +1,12 @@ +import { isMotionValue } from 'motion-dom'; + +/** + * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself + * + * TODO: Remove and move to library + */ +function resolveMotionValue(value) { + return isMotionValue(value) ? value.get() : value; +} + +export { resolveMotionValue }; diff --git a/node_modules/framer-motion/dist/framer-motion.dev.js b/node_modules/framer-motion/dist/framer-motion.dev.js new file mode 100644 index 00000000..e7b4438d --- /dev/null +++ b/node_modules/framer-motion/dist/framer-motion.dev.js @@ -0,0 +1,14262 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : + typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Motion = {}, global.React)); +})(this, (function (exports, React$1) { 'use strict'; + + function _interopNamespaceDefault(e) { + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n.default = e; + return Object.freeze(n); + } + + var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React$1); + + // source: react/cjs/react-jsx-runtime.production.min.js + /** + * @license React + * react-jsx-runtime.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + var f = React, + k = Symbol.for("react.element"), + l = Symbol.for("react.fragment"), + m$1 = Object.prototype.hasOwnProperty, + n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, + p = { key: !0, ref: !0, __self: !0, __source: !0 }; + function q(c, a, g) { + var b, + d = {}, + e = null, + h = null; + void 0 !== g && (e = "" + g); + void 0 !== a.key && (e = "" + a.key); + void 0 !== a.ref && (h = a.ref); + for (b in a) m$1.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]); + if (c && c.defaultProps) + for (b in ((a = c.defaultProps), a)) void 0 === d[b] && (d[b] = a[b]); + return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current } + } + const Fragment = l; + const jsx = q; + const jsxs = q; + + const LayoutGroupContext = React$1.createContext({}); + + /** + * Creates a constant value over the lifecycle of a component. + * + * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer + * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` + * you can ensure that initialisers don't execute twice or more. + */ + function useConstant(init) { + const ref = React$1.useRef(null); + if (ref.current === null) { + ref.current = init(); + } + return ref.current; + } + + const isBrowser = typeof window !== "undefined"; + + const useIsomorphicLayoutEffect = isBrowser ? React$1.useLayoutEffect : React$1.useEffect; + + /** + * @public + */ + const PresenceContext = + /* @__PURE__ */ React$1.createContext(null); + + function addUniqueItem(arr, item) { + if (arr.indexOf(item) === -1) + arr.push(item); + } + function removeItem(arr, item) { + const index = arr.indexOf(item); + if (index > -1) + arr.splice(index, 1); + } + // Adapted from array-move + function moveItem([...arr], fromIndex, toIndex) { + const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex; + if (startIndex >= 0 && startIndex < arr.length) { + const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex; + const [item] = arr.splice(fromIndex, 1); + arr.splice(endIndex, 0, item); + } + return arr; + } + + const clamp = (min, max, v) => { + if (v > max) + return max; + if (v < min) + return min; + return v; + }; + + exports.warning = () => { }; + exports.invariant = () => { }; + { + exports.warning = (check, message) => { + if (!check && typeof console !== "undefined") { + console.warn(message); + } + }; + exports.invariant = (check, message) => { + if (!check) { + throw new Error(message); + } + }; + } + + const MotionGlobalConfig = {}; + + /** + * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1" + */ + const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v); + + function isObject(value) { + return typeof value === "object" && value !== null; + } + + /** + * Check if the value is a zero value string like "0px" or "0%" + */ + const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v); + + /*#__NO_SIDE_EFFECTS__*/ + function memo(callback) { + let result; + return () => { + if (result === undefined) + result = callback(); + return result; + }; + } + + /*#__NO_SIDE_EFFECTS__*/ + const noop = (any) => any; + + /** + * Pipe + * Compose other transformers to run linearily + * pipe(min(20), max(40)) + * @param {...functions} transformers + * @return {function} + */ + const combineFunctions = (a, b) => (v) => b(a(v)); + const pipe = (...transformers) => transformers.reduce(combineFunctions); + + /* + Progress within given range + + Given a lower limit and an upper limit, we return the progress + (expressed as a number 0-1) represented by the given value, and + limit that progress to within 0-1. + + @param [number]: Lower limit + @param [number]: Upper limit + @param [number]: Value to find progress within given range + @return [number]: Progress of value within range as expressed 0-1 + */ + /*#__NO_SIDE_EFFECTS__*/ + const progress = (from, to, value) => { + const toFromDifference = to - from; + return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; + }; + + class SubscriptionManager { + constructor() { + this.subscriptions = []; + } + add(handler) { + addUniqueItem(this.subscriptions, handler); + return () => removeItem(this.subscriptions, handler); + } + notify(a, b, c) { + const numSubscriptions = this.subscriptions.length; + if (!numSubscriptions) + return; + if (numSubscriptions === 1) { + /** + * If there's only a single handler we can just call it without invoking a loop. + */ + this.subscriptions[0](a, b, c); + } + else { + for (let i = 0; i < numSubscriptions; i++) { + /** + * Check whether the handler exists before firing as it's possible + * the subscriptions were modified during this loop running. + */ + const handler = this.subscriptions[i]; + handler && handler(a, b, c); + } + } + } + getSize() { + return this.subscriptions.length; + } + clear() { + this.subscriptions.length = 0; + } + } + + /** + * Converts seconds to milliseconds + * + * @param seconds - Time in seconds. + * @return milliseconds - Converted time in milliseconds. + */ + /*#__NO_SIDE_EFFECTS__*/ + const secondsToMilliseconds = (seconds) => seconds * 1000; + /*#__NO_SIDE_EFFECTS__*/ + const millisecondsToSeconds = (milliseconds) => milliseconds / 1000; + + /* + Convert velocity into velocity per second + + @param [number]: Unit per frame + @param [number]: Frame duration in ms + */ + function velocityPerSecond(velocity, frameDuration) { + return frameDuration ? velocity * (1000 / frameDuration) : 0; + } + + const warned = new Set(); + function hasWarned$1(message) { + return warned.has(message); + } + function warnOnce(condition, message, element) { + if (condition || warned.has(message)) + return; + console.warn(message); + if (element) + console.warn(element); + warned.add(message); + } + + const wrap = (min, max, v) => { + const rangeSize = max - min; + return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min; + }; + + /* + Bezier function generator + This has been modified from Gaëtan Renaudeau's BezierEasing + https://github.com/gre/bezier-easing/blob/master/src/index.js + https://github.com/gre/bezier-easing/blob/master/LICENSE + + I've removed the newtonRaphsonIterate algo because in benchmarking it + wasn't noticeably faster than binarySubdivision, indeed removing it + usually improved times, depending on the curve. + I also removed the lookup table, as for the added bundle size and loop we're + only cutting ~4 or so subdivision iterations. I bumped the max iterations up + to 12 to compensate and this still tended to be faster for no perceivable + loss in accuracy. + Usage + const easeOut = cubicBezier(.17,.67,.83,.67); + const x = easeOut(0.5); // returns 0.627... + */ + // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. + const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * + t; + const subdivisionPrecision = 0.0000001; + const subdivisionMaxIterations = 12; + function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) { + let currentX; + let currentT; + let i = 0; + do { + currentT = lowerBound + (upperBound - lowerBound) / 2.0; + currentX = calcBezier(currentT, mX1, mX2) - x; + if (currentX > 0.0) { + upperBound = currentT; + } + else { + lowerBound = currentT; + } + } while (Math.abs(currentX) > subdivisionPrecision && + ++i < subdivisionMaxIterations); + return currentT; + } + function cubicBezier(mX1, mY1, mX2, mY2) { + // If this is a linear gradient, return linear easing + if (mX1 === mY1 && mX2 === mY2) + return noop; + const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2); + // If animation is at start/end, return t without easing + return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2); + } + + // Accepts an easing function and returns a new one that outputs mirrored values for + // the second half of the animation. Turns easeIn into easeInOut. + const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2; + + // Accepts an easing function and returns a new one that outputs reversed values. + // Turns easeIn into easeOut. + const reverseEasing = (easing) => (p) => 1 - easing(1 - p); + + const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99); + const backIn = /*@__PURE__*/ reverseEasing(backOut); + const backInOut = /*@__PURE__*/ mirrorEasing(backIn); + + const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); + + const circIn = (p) => 1 - Math.sin(Math.acos(p)); + const circOut = reverseEasing(circIn); + const circInOut = mirrorEasing(circIn); + + const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1); + const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1); + const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1); + + function steps(numSteps, direction = "end") { + return (progress) => { + progress = + direction === "end" + ? Math.min(progress, 0.999) + : Math.max(progress, 0.001); + const expanded = progress * numSteps; + const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded); + return clamp(0, 1, rounded / numSteps); + }; + } + + const isEasingArray = (ease) => { + return Array.isArray(ease) && typeof ease[0] !== "number"; + }; + + function getEasingForSegment(easing, i) { + return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing; + } + + const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number"; + + const easingLookup = { + linear: noop, + easeIn, + easeInOut, + easeOut, + circIn, + circInOut, + circOut, + backIn, + backInOut, + backOut, + anticipate, + }; + const isValidEasing = (easing) => { + return typeof easing === "string"; + }; + const easingDefinitionToFunction = (definition) => { + if (isBezierDefinition(definition)) { + // If cubic bezier definition, create bezier curve + exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`); + const [x1, y1, x2, y2] = definition; + return cubicBezier(x1, y1, x2, y2); + } + else if (isValidEasing(definition)) { + // Else lookup from table + exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`); + return easingLookup[definition]; + } + return definition; + }; + + const stepsOrder = [ + "setup", // Compute + "read", // Read + "resolveKeyframes", // Write/Read/Write/Read + "preUpdate", // Compute + "update", // Compute + "preRender", // Compute + "render", // Write + "postRender", // Compute + ]; + + const statsBuffer = { + value: null, + addProjectionMetrics: null, + }; + + function createRenderStep(runNextFrame, stepName) { + /** + * We create and reuse two queues, one to queue jobs for the current frame + * and one for the next. We reuse to avoid triggering GC after x frames. + */ + let thisFrame = new Set(); + let nextFrame = new Set(); + /** + * Track whether we're currently processing jobs in this step. This way + * we can decide whether to schedule new jobs for this frame or next. + */ + let isProcessing = false; + let flushNextFrame = false; + /** + * A set of processes which were marked keepAlive when scheduled. + */ + const toKeepAlive = new WeakSet(); + let latestFrameData = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + let numCalls = 0; + function triggerCallback(callback) { + if (toKeepAlive.has(callback)) { + step.schedule(callback); + runNextFrame(); + } + numCalls++; + callback(latestFrameData); + } + const step = { + /** + * Schedule a process to run on the next frame. + */ + schedule: (callback, keepAlive = false, immediate = false) => { + const addToCurrentFrame = immediate && isProcessing; + const queue = addToCurrentFrame ? thisFrame : nextFrame; + if (keepAlive) + toKeepAlive.add(callback); + if (!queue.has(callback)) + queue.add(callback); + return callback; + }, + /** + * Cancel the provided callback from running on the next frame. + */ + cancel: (callback) => { + nextFrame.delete(callback); + toKeepAlive.delete(callback); + }, + /** + * Execute all schedule callbacks. + */ + process: (frameData) => { + latestFrameData = frameData; + /** + * If we're already processing we've probably been triggered by a flushSync + * inside an existing process. Instead of executing, mark flushNextFrame + * as true and ensure we flush the following frame at the end of this one. + */ + if (isProcessing) { + flushNextFrame = true; + return; + } + isProcessing = true; + [thisFrame, nextFrame] = [nextFrame, thisFrame]; + // Execute this frame + thisFrame.forEach(triggerCallback); + /** + * If we're recording stats then + */ + if (stepName && statsBuffer.value) { + statsBuffer.value.frameloop[stepName].push(numCalls); + } + numCalls = 0; + // Clear the frame so no callbacks remain. This is to avoid + // memory leaks should this render step not run for a while. + thisFrame.clear(); + isProcessing = false; + if (flushNextFrame) { + flushNextFrame = false; + step.process(frameData); + } + }, + }; + return step; + } + + const maxElapsed$1 = 40; + function createRenderBatcher(scheduleNextBatch, allowKeepAlive) { + let runNextFrame = false; + let useDefaultElapsed = true; + const state = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + const flagRunNextFrame = () => (runNextFrame = true); + const steps = stepsOrder.reduce((acc, key) => { + acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined); + return acc; + }, {}); + const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps; + const processBatch = () => { + const timestamp = MotionGlobalConfig.useManualTiming + ? state.timestamp + : performance.now(); + runNextFrame = false; + if (!MotionGlobalConfig.useManualTiming) { + state.delta = useDefaultElapsed + ? 1000 / 60 + : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1); + } + state.timestamp = timestamp; + state.isProcessing = true; + // Unrolled render loop for better per-frame performance + setup.process(state); + read.process(state); + resolveKeyframes.process(state); + preUpdate.process(state); + update.process(state); + preRender.process(state); + render.process(state); + postRender.process(state); + state.isProcessing = false; + if (runNextFrame && allowKeepAlive) { + useDefaultElapsed = false; + scheduleNextBatch(processBatch); + } + }; + const wake = () => { + runNextFrame = true; + useDefaultElapsed = true; + if (!state.isProcessing) { + scheduleNextBatch(processBatch); + } + }; + const schedule = stepsOrder.reduce((acc, key) => { + const step = steps[key]; + acc[key] = (process, keepAlive = false, immediate = false) => { + if (!runNextFrame) + wake(); + return step.schedule(process, keepAlive, immediate); + }; + return acc; + }, {}); + const cancel = (process) => { + for (let i = 0; i < stepsOrder.length; i++) { + steps[stepsOrder[i]].cancel(process); + } + }; + return { schedule, cancel, state, steps }; + } + + const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true); + + let now; + function clearTime() { + now = undefined; + } + /** + * An eventloop-synchronous alternative to performance.now(). + * + * Ensures that time measurements remain consistent within a synchronous context. + * Usually calling performance.now() twice within the same synchronous context + * will return different values which isn't useful for animations when we're usually + * trying to sync animations to the same frame. + */ + const time = { + now: () => { + if (now === undefined) { + time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming + ? frameData.timestamp + : performance.now()); + } + return now; + }, + set: (newTime) => { + now = newTime; + queueMicrotask(clearTime); + }, + }; + + const activeAnimations = { + layout: 0, + mainThread: 0, + waapi: 0, + }; + + const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token); + const isCSSVariableName = + /*@__PURE__*/ checkStringStartsWith("--"); + const startsAsVariableToken = + /*@__PURE__*/ checkStringStartsWith("var(--"); + const isCSSVariableToken = (value) => { + const startsWithToken = startsAsVariableToken(value); + if (!startsWithToken) + return false; + // Ensure any comments are stripped from the value as this can harm performance of the regex. + return singleCssVariableRegex.test(value.split("/*")[0].trim()); + }; + const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu; + + const number = { + test: (v) => typeof v === "number", + parse: parseFloat, + transform: (v) => v, + }; + const alpha = { + ...number, + transform: (v) => clamp(0, 1, v), + }; + const scale = { + ...number, + default: 1, + }; + + // If this number is a decimal, make it just five decimal places + // to avoid exponents + const sanitize = (v) => Math.round(v * 100000) / 100000; + + const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu; + + function isNullish(v) { + return v == null; + } + + const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu; + + /** + * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000, + * but false if a number or multiple colors + */ + const isColorString = (type, testProp) => (v) => { + return Boolean((typeof v === "string" && + singleColorRegex.test(v) && + v.startsWith(type)) || + (testProp && + !isNullish(v) && + Object.prototype.hasOwnProperty.call(v, testProp))); + }; + const splitColor = (aName, bName, cName) => (v) => { + if (typeof v !== "string") + return v; + const [a, b, c, alpha] = v.match(floatRegex); + return { + [aName]: parseFloat(a), + [bName]: parseFloat(b), + [cName]: parseFloat(c), + alpha: alpha !== undefined ? parseFloat(alpha) : 1, + }; + }; + + const clampRgbUnit = (v) => clamp(0, 255, v); + const rgbUnit = { + ...number, + transform: (v) => Math.round(clampRgbUnit(v)), + }; + const rgba = { + test: /*@__PURE__*/ isColorString("rgb", "red"), + parse: /*@__PURE__*/ splitColor("red", "green", "blue"), + transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + + rgbUnit.transform(red) + + ", " + + rgbUnit.transform(green) + + ", " + + rgbUnit.transform(blue) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")", + }; + + function parseHex(v) { + let r = ""; + let g = ""; + let b = ""; + let a = ""; + // If we have 6 characters, ie #FF0000 + if (v.length > 5) { + r = v.substring(1, 3); + g = v.substring(3, 5); + b = v.substring(5, 7); + a = v.substring(7, 9); + // Or we have 3 characters, ie #F00 + } + else { + r = v.substring(1, 2); + g = v.substring(2, 3); + b = v.substring(3, 4); + a = v.substring(4, 5); + r += r; + g += g; + b += b; + a += a; + } + return { + red: parseInt(r, 16), + green: parseInt(g, 16), + blue: parseInt(b, 16), + alpha: a ? parseInt(a, 16) / 255 : 1, + }; + } + const hex = { + test: /*@__PURE__*/ isColorString("#"), + parse: parseHex, + transform: rgba.transform, + }; + + /*#__NO_SIDE_EFFECTS__*/ + const createUnitType = (unit) => ({ + test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1, + parse: parseFloat, + transform: (v) => `${v}${unit}`, + }); + const degrees = /*@__PURE__*/ createUnitType("deg"); + const percent = /*@__PURE__*/ createUnitType("%"); + const px = /*@__PURE__*/ createUnitType("px"); + const vh = /*@__PURE__*/ createUnitType("vh"); + const vw = /*@__PURE__*/ createUnitType("vw"); + const progressPercentage = /*@__PURE__*/ (() => ({ + ...percent, + parse: (v) => percent.parse(v) / 100, + transform: (v) => percent.transform(v * 100), + }))(); + + const hsla = { + test: /*@__PURE__*/ isColorString("hsl", "hue"), + parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"), + transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { + return ("hsla(" + + Math.round(hue) + + ", " + + percent.transform(sanitize(saturation)) + + ", " + + percent.transform(sanitize(lightness)) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")"); + }, + }; + + const color = { + test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v), + parse: (v) => { + if (rgba.test(v)) { + return rgba.parse(v); + } + else if (hsla.test(v)) { + return hsla.parse(v); + } + else { + return hex.parse(v); + } + }, + transform: (v) => { + return typeof v === "string" + ? v + : v.hasOwnProperty("red") + ? rgba.transform(v) + : hsla.transform(v); + }, + getAnimatableNone: (v) => { + const parsed = color.parse(v); + parsed.alpha = 0; + return color.transform(parsed); + }, + }; + + const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu; + + function test(v) { + return (isNaN(v) && + typeof v === "string" && + (v.match(floatRegex)?.length || 0) + + (v.match(colorRegex)?.length || 0) > + 0); + } + const NUMBER_TOKEN = "number"; + const COLOR_TOKEN = "color"; + const VAR_TOKEN = "var"; + const VAR_FUNCTION_TOKEN = "var("; + const SPLIT_TOKEN = "${}"; + // this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex` + const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu; + function analyseComplexValue(value) { + const originalValue = value.toString(); + const values = []; + const indexes = { + color: [], + number: [], + var: [], + }; + const types = []; + let i = 0; + const tokenised = originalValue.replace(complexRegex, (parsedValue) => { + if (color.test(parsedValue)) { + indexes.color.push(i); + types.push(COLOR_TOKEN); + values.push(color.parse(parsedValue)); + } + else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) { + indexes.var.push(i); + types.push(VAR_TOKEN); + values.push(parsedValue); + } + else { + indexes.number.push(i); + types.push(NUMBER_TOKEN); + values.push(parseFloat(parsedValue)); + } + ++i; + return SPLIT_TOKEN; + }); + const split = tokenised.split(SPLIT_TOKEN); + return { values, split, indexes, types }; + } + function parseComplexValue(v) { + return analyseComplexValue(v).values; + } + function createTransformer(source) { + const { split, types } = analyseComplexValue(source); + const numSections = split.length; + return (v) => { + let output = ""; + for (let i = 0; i < numSections; i++) { + output += split[i]; + if (v[i] !== undefined) { + const type = types[i]; + if (type === NUMBER_TOKEN) { + output += sanitize(v[i]); + } + else if (type === COLOR_TOKEN) { + output += color.transform(v[i]); + } + else { + output += v[i]; + } + } + } + return output; + }; + } + const convertNumbersToZero = (v) => typeof v === "number" ? 0 : color.test(v) ? color.getAnimatableNone(v) : v; + function getAnimatableNone$1(v) { + const parsed = parseComplexValue(v); + const transformer = createTransformer(v); + return transformer(parsed.map(convertNumbersToZero)); + } + const complex = { + test, + parse: parseComplexValue, + createTransformer, + getAnimatableNone: getAnimatableNone$1, + }; + + // Adapted from https://gist.github.com/mjackson/5311256 + function hueToRgb(p, q, t) { + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < 1 / 6) + return p + (q - p) * 6 * t; + if (t < 1 / 2) + return q; + if (t < 2 / 3) + return p + (q - p) * (2 / 3 - t) * 6; + return p; + } + function hslaToRgba({ hue, saturation, lightness, alpha }) { + hue /= 360; + saturation /= 100; + lightness /= 100; + let red = 0; + let green = 0; + let blue = 0; + if (!saturation) { + red = green = blue = lightness; + } + else { + const q = lightness < 0.5 + ? lightness * (1 + saturation) + : lightness + saturation - lightness * saturation; + const p = 2 * lightness - q; + red = hueToRgb(p, q, hue + 1 / 3); + green = hueToRgb(p, q, hue); + blue = hueToRgb(p, q, hue - 1 / 3); + } + return { + red: Math.round(red * 255), + green: Math.round(green * 255), + blue: Math.round(blue * 255), + alpha, + }; + } + + function mixImmediate(a, b) { + return (p) => (p > 0 ? b : a); + } + + /* + Value in range from progress + + Given a lower limit and an upper limit, we return the value within + that range as expressed by progress (usually a number from 0 to 1) + + So progress = 0.5 would change + + from -------- to + + to + + from ---- to + + E.g. from = 10, to = 20, progress = 0.5 => 15 + + @param [number]: Lower limit of range + @param [number]: Upper limit of range + @param [number]: The progress between lower and upper limits expressed 0-1 + @return [number]: Value as calculated from progress within range (not limited within range) + */ + const mixNumber$1 = (from, to, progress) => { + return from + (to - from) * progress; + }; + + // Linear color space blending + // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw + // Demonstrated http://codepen.io/osublake/pen/xGVVaN + const mixLinearColor = (from, to, v) => { + const fromExpo = from * from; + const expo = v * (to * to - fromExpo) + fromExpo; + return expo < 0 ? 0 : Math.sqrt(expo); + }; + const colorTypes = [hex, rgba, hsla]; + const getColorType = (v) => colorTypes.find((type) => type.test(v)); + function asRGBA(color) { + const type = getColorType(color); + exports.warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`); + if (!Boolean(type)) + return false; + let model = type.parse(color); + if (type === hsla) { + // TODO Remove this cast - needed since Motion's stricter typing + model = hslaToRgba(model); + } + return model; + } + const mixColor = (from, to) => { + const fromRGBA = asRGBA(from); + const toRGBA = asRGBA(to); + if (!fromRGBA || !toRGBA) { + return mixImmediate(from, to); + } + const blended = { ...fromRGBA }; + return (v) => { + blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v); + blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v); + blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v); + blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v); + return rgba.transform(blended); + }; + }; + + const invisibleValues = new Set(["none", "hidden"]); + /** + * Returns a function that, when provided a progress value between 0 and 1, + * will return the "none" or "hidden" string only when the progress is that of + * the origin or target. + */ + function mixVisibility(origin, target) { + if (invisibleValues.has(origin)) { + return (p) => (p <= 0 ? origin : target); + } + else { + return (p) => (p >= 1 ? target : origin); + } + } + + function mixNumber(a, b) { + return (p) => mixNumber$1(a, b, p); + } + function getMixer(a) { + if (typeof a === "number") { + return mixNumber; + } + else if (typeof a === "string") { + return isCSSVariableToken(a) + ? mixImmediate + : color.test(a) + ? mixColor + : mixComplex; + } + else if (Array.isArray(a)) { + return mixArray; + } + else if (typeof a === "object") { + return color.test(a) ? mixColor : mixObject; + } + return mixImmediate; + } + function mixArray(a, b) { + const output = [...a]; + const numValues = output.length; + const blendValue = a.map((v, i) => getMixer(v)(v, b[i])); + return (p) => { + for (let i = 0; i < numValues; i++) { + output[i] = blendValue[i](p); + } + return output; + }; + } + function mixObject(a, b) { + const output = { ...a, ...b }; + const blendValue = {}; + for (const key in output) { + if (a[key] !== undefined && b[key] !== undefined) { + blendValue[key] = getMixer(a[key])(a[key], b[key]); + } + } + return (v) => { + for (const key in blendValue) { + output[key] = blendValue[key](v); + } + return output; + }; + } + function matchOrder(origin, target) { + const orderedOrigin = []; + const pointers = { color: 0, var: 0, number: 0 }; + for (let i = 0; i < target.values.length; i++) { + const type = target.types[i]; + const originIndex = origin.indexes[type][pointers[type]]; + const originValue = origin.values[originIndex] ?? 0; + orderedOrigin[i] = originValue; + pointers[type]++; + } + return orderedOrigin; + } + const mixComplex = (origin, target) => { + const template = complex.createTransformer(target); + const originStats = analyseComplexValue(origin); + const targetStats = analyseComplexValue(target); + const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length && + originStats.indexes.color.length === targetStats.indexes.color.length && + originStats.indexes.number.length >= targetStats.indexes.number.length; + if (canInterpolate) { + if ((invisibleValues.has(origin) && + !targetStats.values.length) || + (invisibleValues.has(target) && + !originStats.values.length)) { + return mixVisibility(origin, target); + } + return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template); + } + else { + exports.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`); + return mixImmediate(origin, target); + } + }; + + function mix(from, to, p) { + if (typeof from === "number" && + typeof to === "number" && + typeof p === "number") { + return mixNumber$1(from, to, p); + } + const mixer = getMixer(from); + return mixer(from, to); + } + + const frameloopDriver = (update) => { + const passTimestamp = ({ timestamp }) => update(timestamp); + return { + start: (keepAlive = true) => frame.update(passTimestamp, keepAlive), + stop: () => cancelFrame(passTimestamp), + /** + * If we're processing this frame we can use the + * framelocked timestamp to keep things in sync. + */ + now: () => (frameData.isProcessing ? frameData.timestamp : time.now()), + }; + }; + + const generateLinearEasing = (easing, duration, // as milliseconds + resolution = 10 // as milliseconds + ) => { + let points = ""; + const numPoints = Math.max(Math.round(duration / resolution), 2); + for (let i = 0; i < numPoints; i++) { + points += Math.round(easing(i / (numPoints - 1)) * 10000) / 10000 + ", "; + } + return `linear(${points.substring(0, points.length - 2)})`; + }; + + /** + * Implement a practical max duration for keyframe generation + * to prevent infinite loops + */ + const maxGeneratorDuration = 20000; + function calcGeneratorDuration(generator) { + let duration = 0; + const timeStep = 50; + let state = generator.next(duration); + while (!state.done && duration < maxGeneratorDuration) { + duration += timeStep; + state = generator.next(duration); + } + return duration >= maxGeneratorDuration ? Infinity : duration; + } + + /** + * Create a progress => progress easing function from a generator. + */ + function createGeneratorEasing(options, scale = 100, createGenerator) { + const generator = createGenerator({ ...options, keyframes: [0, scale] }); + const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + return { + type: "keyframes", + ease: (progress) => { + return generator.next(duration * progress).value / scale; + }, + duration: millisecondsToSeconds(duration), + }; + } + + const velocitySampleDuration = 5; // ms + function calcGeneratorVelocity(resolveValue, t, current) { + const prevT = Math.max(t - velocitySampleDuration, 0); + return velocityPerSecond(current - resolveValue(prevT), t - prevT); + } + + const springDefaults = { + // Default spring physics + stiffness: 100, + damping: 10, + mass: 1.0, + velocity: 0.0, + // Default duration/bounce-based options + duration: 800, // in ms + bounce: 0.3, + visualDuration: 0.3, // in seconds + // Rest thresholds + restSpeed: { + granular: 0.01, + default: 2, + }, + restDelta: { + granular: 0.005, + default: 0.5, + }, + // Limits + minDuration: 0.01, // in seconds + maxDuration: 10.0, // in seconds + minDamping: 0.05, + maxDamping: 1, + }; + + const safeMin = 0.001; + function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) { + let envelope; + let derivative; + exports.warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less"); + let dampingRatio = 1 - bounce; + /** + * Restrict dampingRatio and duration to within acceptable ranges. + */ + dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio); + duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration)); + if (dampingRatio < 1) { + /** + * Underdamped spring + */ + envelope = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const a = exponentialDecay - velocity; + const b = calcAngularFreq(undampedFreq, dampingRatio); + const c = Math.exp(-delta); + return safeMin - (a / b) * c; + }; + derivative = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const d = delta * velocity + velocity; + const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration; + const f = Math.exp(-delta); + const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio); + const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1; + return (factor * ((d - e) * f)) / g; + }; + } + else { + /** + * Critically-damped spring + */ + envelope = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (undampedFreq - velocity) * duration + 1; + return -safeMin + a * b; + }; + derivative = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (velocity - undampedFreq) * (duration * duration); + return a * b; + }; + } + const initialGuess = 5 / duration; + const undampedFreq = approximateRoot(envelope, derivative, initialGuess); + duration = secondsToMilliseconds(duration); + if (isNaN(undampedFreq)) { + return { + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + duration, + }; + } + else { + const stiffness = Math.pow(undampedFreq, 2) * mass; + return { + stiffness, + damping: dampingRatio * 2 * Math.sqrt(mass * stiffness), + duration, + }; + } + } + const rootIterations = 12; + function approximateRoot(envelope, derivative, initialGuess) { + let result = initialGuess; + for (let i = 1; i < rootIterations; i++) { + result = result - envelope(result) / derivative(result); + } + return result; + } + function calcAngularFreq(undampedFreq, dampingRatio) { + return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio); + } + + const durationKeys = ["duration", "bounce"]; + const physicsKeys = ["stiffness", "damping", "mass"]; + function isSpringType(options, keys) { + return keys.some((key) => options[key] !== undefined); + } + function getSpringOptions(options) { + let springOptions = { + velocity: springDefaults.velocity, + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + mass: springDefaults.mass, + isResolvedFromDuration: false, + ...options, + }; + // stiffness/damping/mass overrides duration/bounce + if (!isSpringType(options, physicsKeys) && + isSpringType(options, durationKeys)) { + if (options.visualDuration) { + const visualDuration = options.visualDuration; + const root = (2 * Math.PI) / (visualDuration * 1.2); + const stiffness = root * root; + const damping = 2 * + clamp(0.05, 1, 1 - (options.bounce || 0)) * + Math.sqrt(stiffness); + springOptions = { + ...springOptions, + mass: springDefaults.mass, + stiffness, + damping, + }; + } + else { + const derived = findSpring(options); + springOptions = { + ...springOptions, + ...derived, + mass: springDefaults.mass, + }; + springOptions.isResolvedFromDuration = true; + } + } + return springOptions; + } + function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) { + const options = typeof optionsOrVisualDuration !== "object" + ? { + visualDuration: optionsOrVisualDuration, + keyframes: [0, 1], + bounce, + } + : optionsOrVisualDuration; + let { restSpeed, restDelta } = options; + const origin = options.keyframes[0]; + const target = options.keyframes[options.keyframes.length - 1]; + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { done: false, value: origin }; + const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({ + ...options, + velocity: -millisecondsToSeconds(options.velocity || 0), + }); + const initialVelocity = velocity || 0.0; + const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass)); + const initialDelta = target - origin; + const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass)); + /** + * If we're working on a granular scale, use smaller defaults for determining + * when the spring is finished. + * + * These defaults have been selected emprically based on what strikes a good + * ratio between feeling good and finishing as soon as changes are imperceptible. + */ + const isGranularScale = Math.abs(initialDelta) < 5; + restSpeed || (restSpeed = isGranularScale + ? springDefaults.restSpeed.granular + : springDefaults.restSpeed.default); + restDelta || (restDelta = isGranularScale + ? springDefaults.restDelta.granular + : springDefaults.restDelta.default); + let resolveSpring; + if (dampingRatio < 1) { + const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio); + // Underdamped spring + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + return (target - + envelope * + (((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) / + angularFreq) * + Math.sin(angularFreq * t) + + initialDelta * Math.cos(angularFreq * t))); + }; + } + else if (dampingRatio === 1) { + // Critically damped spring + resolveSpring = (t) => target - + Math.exp(-undampedAngularFreq * t) * + (initialDelta + + (initialVelocity + undampedAngularFreq * initialDelta) * t); + } + else { + // Overdamped spring + const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1); + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + // When performing sinh or cosh values can hit Infinity so we cap them here + const freqForT = Math.min(dampedAngularFreq * t, 300); + return (target - + (envelope * + ((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) * + Math.sinh(freqForT) + + dampedAngularFreq * + initialDelta * + Math.cosh(freqForT))) / + dampedAngularFreq); + }; + } + const generator = { + calculatedDuration: isResolvedFromDuration ? duration || null : null, + next: (t) => { + const current = resolveSpring(t); + if (!isResolvedFromDuration) { + let currentVelocity = t === 0 ? initialVelocity : 0.0; + /** + * We only need to calculate velocity for under-damped springs + * as over- and critically-damped springs can't overshoot, so + * checking only for displacement is enough. + */ + if (dampingRatio < 1) { + currentVelocity = + t === 0 + ? secondsToMilliseconds(initialVelocity) + : calcGeneratorVelocity(resolveSpring, t, current); + } + const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed; + const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta; + state.done = + isBelowVelocityThreshold && isBelowDisplacementThreshold; + } + else { + state.done = t >= duration; + } + state.value = state.done ? target : current; + return state; + }, + toString: () => { + const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30); + return calculatedDuration + "ms " + easing; + }, + toTransition: () => { }, + }; + return generator; + } + spring.applyToOptions = (options) => { + const generatorOptions = createGeneratorEasing(options, 100, spring); + options.ease = generatorOptions.ease; + options.duration = secondsToMilliseconds(generatorOptions.duration); + options.type = "keyframes"; + return options; + }; + + function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) { + const origin = keyframes[0]; + const state = { + done: false, + value: origin, + }; + const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max); + const nearestBoundary = (v) => { + if (min === undefined) + return max; + if (max === undefined) + return min; + return Math.abs(min - v) < Math.abs(max - v) ? min : max; + }; + let amplitude = power * velocity; + const ideal = origin + amplitude; + const target = modifyTarget === undefined ? ideal : modifyTarget(ideal); + /** + * If the target has changed we need to re-calculate the amplitude, otherwise + * the animation will start from the wrong position. + */ + if (target !== ideal) + amplitude = target - origin; + const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant); + const calcLatest = (t) => target + calcDelta(t); + const applyFriction = (t) => { + const delta = calcDelta(t); + const latest = calcLatest(t); + state.done = Math.abs(delta) <= restDelta; + state.value = state.done ? target : latest; + }; + /** + * Ideally this would resolve for t in a stateless way, we could + * do that by always precalculating the animation but as we know + * this will be done anyway we can assume that spring will + * be discovered during that. + */ + let timeReachedBoundary; + let spring$1; + const checkCatchBoundary = (t) => { + if (!isOutOfBounds(state.value)) + return; + timeReachedBoundary = t; + spring$1 = spring({ + keyframes: [state.value, nearestBoundary(state.value)], + velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000 + damping: bounceDamping, + stiffness: bounceStiffness, + restDelta, + restSpeed, + }); + }; + checkCatchBoundary(0); + return { + calculatedDuration: null, + next: (t) => { + /** + * We need to resolve the friction to figure out if we need a + * spring but we don't want to do this twice per frame. So here + * we flag if we updated for this frame and later if we did + * we can skip doing it again. + */ + let hasUpdatedFrame = false; + if (!spring$1 && timeReachedBoundary === undefined) { + hasUpdatedFrame = true; + applyFriction(t); + checkCatchBoundary(t); + } + /** + * If we have a spring and the provided t is beyond the moment the friction + * animation crossed the min/max boundary, use the spring. + */ + if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) { + return spring$1.next(t - timeReachedBoundary); + } + else { + !hasUpdatedFrame && applyFriction(t); + return state; + } + }, + }; + } + + function createMixers(output, ease, customMixer) { + const mixers = []; + const mixerFactory = customMixer || MotionGlobalConfig.mix || mix; + const numMixers = output.length - 1; + for (let i = 0; i < numMixers; i++) { + let mixer = mixerFactory(output[i], output[i + 1]); + if (ease) { + const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease; + mixer = pipe(easingFunction, mixer); + } + mixers.push(mixer); + } + return mixers; + } + /** + * Create a function that maps from a numerical input array to a generic output array. + * + * Accepts: + * - Numbers + * - Colors (hex, hsl, hsla, rgb, rgba) + * - Complex (combinations of one or more numbers or strings) + * + * ```jsx + * const mixColor = interpolate([0, 1], ['#fff', '#000']) + * + * mixColor(0.5) // 'rgba(128, 128, 128, 1)' + * ``` + * + * TODO Revisit this approach once we've moved to data models for values, + * probably not needed to pregenerate mixer functions. + * + * @public + */ + function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) { + const inputLength = input.length; + exports.invariant(inputLength === output.length, "Both input and output ranges must be the same length"); + /** + * If we're only provided a single input, we can just make a function + * that returns the output. + */ + if (inputLength === 1) + return () => output[0]; + if (inputLength === 2 && output[0] === output[1]) + return () => output[1]; + const isZeroDeltaRange = input[0] === input[1]; + // If input runs highest -> lowest, reverse both arrays + if (input[0] > input[inputLength - 1]) { + input = [...input].reverse(); + output = [...output].reverse(); + } + const mixers = createMixers(output, ease, mixer); + const numMixers = mixers.length; + const interpolator = (v) => { + if (isZeroDeltaRange && v < input[0]) + return output[0]; + let i = 0; + if (numMixers > 1) { + for (; i < input.length - 2; i++) { + if (v < input[i + 1]) + break; + } + } + const progressInRange = progress(input[i], input[i + 1], v); + return mixers[i](progressInRange); + }; + return isClamp + ? (v) => interpolator(clamp(input[0], input[inputLength - 1], v)) + : interpolator; + } + + function fillOffset(offset, remaining) { + const min = offset[offset.length - 1]; + for (let i = 1; i <= remaining; i++) { + const offsetProgress = progress(0, remaining, i); + offset.push(mixNumber$1(min, 1, offsetProgress)); + } + } + + function defaultOffset$1(arr) { + const offset = [0]; + fillOffset(offset, arr.length - 1); + return offset; + } + + function convertOffsetToTimes(offset, duration) { + return offset.map((o) => o * duration); + } + + function defaultEasing(values, easing) { + return values.map(() => easing || easeInOut).splice(0, values.length - 1); + } + function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) { + /** + * Easing functions can be externally defined as strings. Here we convert them + * into actual functions. + */ + const easingFunctions = isEasingArray(ease) + ? ease.map(easingDefinitionToFunction) + : easingDefinitionToFunction(ease); + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { + done: false, + value: keyframeValues[0], + }; + /** + * Create a times array based on the provided 0-1 offsets + */ + const absoluteTimes = convertOffsetToTimes( + // Only use the provided offsets if they're the correct length + // TODO Maybe we should warn here if there's a length mismatch + times && times.length === keyframeValues.length + ? times + : defaultOffset$1(keyframeValues), duration); + const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, { + ease: Array.isArray(easingFunctions) + ? easingFunctions + : defaultEasing(keyframeValues, easingFunctions), + }); + return { + calculatedDuration: duration, + next: (t) => { + state.value = mapTimeToKeyframe(t); + state.done = t >= duration; + return state; + }, + }; + } + + const isNotNull$1 = (value) => value !== null; + function getFinalKeyframe$1(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) { + const resolvedKeyframes = keyframes.filter(isNotNull$1); + const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1); + const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; + } + + const transitionTypeMap = { + decay: inertia, + inertia, + tween: keyframes, + keyframes: keyframes, + spring, + }; + function replaceTransitionType(transition) { + if (typeof transition.type === "string") { + transition.type = transitionTypeMap[transition.type]; + } + } + + class WithPromise { + constructor() { + this.updateFinished(); + } + get finished() { + return this._finished; + } + updateFinished() { + this._finished = new Promise((resolve) => { + this.resolve = resolve; + }); + } + notifyFinished() { + this.resolve(); + } + /** + * Allows the animation to be awaited. + * + * @deprecated Use `finished` instead. + */ + then(onResolve, onReject) { + return this.finished.then(onResolve, onReject); + } + } + + const percentToProgress = (percent) => percent / 100; + class JSAnimation extends WithPromise { + constructor(options) { + super(); + this.state = "idle"; + this.startTime = null; + this.isStopped = false; + /** + * The current time of the animation. + */ + this.currentTime = 0; + /** + * The time at which the animation was paused. + */ + this.holdTime = null; + /** + * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed. + */ + this.playbackSpeed = 1; + /** + * This method is bound to the instance to fix a pattern where + * animation.stop is returned as a reference from a useEffect. + */ + this.stop = () => { + const { motionValue } = this.options; + if (motionValue && motionValue.updatedAt !== time.now()) { + this.tick(time.now()); + } + this.isStopped = true; + if (this.state === "idle") + return; + this.teardown(); + this.options.onStop?.(); + }; + activeAnimations.mainThread++; + this.options = options; + this.initAnimation(); + this.play(); + if (options.autoplay === false) + this.pause(); + } + initAnimation() { + const { options } = this; + replaceTransitionType(options); + const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options; + let { keyframes: keyframes$1 } = options; + const generatorFactory = type || keyframes; + if (generatorFactory !== keyframes) { + exports.invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`); + } + if (generatorFactory !== keyframes && + typeof keyframes$1[0] !== "number") { + this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1])); + keyframes$1 = [0, 100]; + } + const generator = generatorFactory({ ...options, keyframes: keyframes$1 }); + /** + * If we have a mirror repeat type we need to create a second generator that outputs the + * mirrored (not reversed) animation and later ping pong between the two generators. + */ + if (repeatType === "mirror") { + this.mirroredGenerator = generatorFactory({ + ...options, + keyframes: [...keyframes$1].reverse(), + velocity: -velocity, + }); + } + /** + * If duration is undefined and we have repeat options, + * we need to calculate a duration from the generator. + * + * We set it to the generator itself to cache the duration. + * Any timeline resolver will need to have already precalculated + * the duration by this step. + */ + if (generator.calculatedDuration === null) { + generator.calculatedDuration = calcGeneratorDuration(generator); + } + const { calculatedDuration } = generator; + this.calculatedDuration = calculatedDuration; + this.resolvedDuration = calculatedDuration + repeatDelay; + this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay; + this.generator = generator; + } + updateTime(timestamp) { + const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed; + // Update currentTime + if (this.holdTime !== null) { + this.currentTime = this.holdTime; + } + else { + // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 = + // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for + // example. + this.currentTime = animationTime; + } + } + tick(timestamp, sample = false) { + const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this; + if (this.startTime === null) + return generator.next(0); + const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options; + /** + * requestAnimationFrame timestamps can come through as lower than + * the startTime as set by performance.now(). Here we prevent this, + * though in the future it could be possible to make setting startTime + * a pending operation that gets resolved here. + */ + if (this.speed > 0) { + this.startTime = Math.min(this.startTime, timestamp); + } + else if (this.speed < 0) { + this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime); + } + if (sample) { + this.currentTime = timestamp; + } + else { + this.updateTime(timestamp); + } + // Rebase on delay + const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1); + const isInDelayPhase = this.playbackSpeed >= 0 + ? timeWithoutDelay < 0 + : timeWithoutDelay > totalDuration; + this.currentTime = Math.max(timeWithoutDelay, 0); + // If this animation has finished, set the current time to the total duration. + if (this.state === "finished" && this.holdTime === null) { + this.currentTime = totalDuration; + } + let elapsed = this.currentTime; + let frameGenerator = generator; + if (repeat) { + /** + * Get the current progress (0-1) of the animation. If t is > + * than duration we'll get values like 2.5 (midway through the + * third iteration) + */ + const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration; + /** + * Get the current iteration (0 indexed). For instance the floor of + * 2.5 is 2. + */ + let currentIteration = Math.floor(progress); + /** + * Get the current progress of the iteration by taking the remainder + * so 2.5 is 0.5 through iteration 2 + */ + let iterationProgress = progress % 1.0; + /** + * If iteration progress is 1 we count that as the end + * of the previous iteration. + */ + if (!iterationProgress && progress >= 1) { + iterationProgress = 1; + } + iterationProgress === 1 && currentIteration--; + currentIteration = Math.min(currentIteration, repeat + 1); + /** + * Reverse progress if we're not running in "normal" direction + */ + const isOddIteration = Boolean(currentIteration % 2); + if (isOddIteration) { + if (repeatType === "reverse") { + iterationProgress = 1 - iterationProgress; + if (repeatDelay) { + iterationProgress -= repeatDelay / resolvedDuration; + } + } + else if (repeatType === "mirror") { + frameGenerator = mirroredGenerator; + } + } + elapsed = clamp(0, 1, iterationProgress) * resolvedDuration; + } + /** + * If we're in negative time, set state as the initial keyframe. + * This prevents delay: x, duration: 0 animations from finishing + * instantly. + */ + const state = isInDelayPhase + ? { done: false, value: keyframes[0] } + : frameGenerator.next(elapsed); + if (mixKeyframes) { + state.value = mixKeyframes(state.value); + } + let { done } = state; + if (!isInDelayPhase && calculatedDuration !== null) { + done = + this.playbackSpeed >= 0 + ? this.currentTime >= totalDuration + : this.currentTime <= 0; + } + const isAnimationFinished = this.holdTime === null && + (this.state === "finished" || (this.state === "running" && done)); + // TODO: The exception for inertia could be cleaner here + if (isAnimationFinished && type !== inertia) { + state.value = getFinalKeyframe$1(keyframes, this.options, finalKeyframe, this.speed); + } + if (onUpdate) { + onUpdate(state.value); + } + if (isAnimationFinished) { + this.finish(); + } + return state; + } + /** + * Allows the returned animation to be awaited or promise-chained. Currently + * resolves when the animation finishes at all but in a future update could/should + * reject if its cancels. + */ + then(resolve, reject) { + return this.finished.then(resolve, reject); + } + get duration() { + return millisecondsToSeconds(this.calculatedDuration); + } + get time() { + return millisecondsToSeconds(this.currentTime); + } + set time(newTime) { + newTime = secondsToMilliseconds(newTime); + this.currentTime = newTime; + if (this.startTime === null || + this.holdTime !== null || + this.playbackSpeed === 0) { + this.holdTime = newTime; + } + else if (this.driver) { + this.startTime = this.driver.now() - newTime / this.playbackSpeed; + } + this.driver?.start(false); + } + get speed() { + return this.playbackSpeed; + } + set speed(newSpeed) { + this.updateTime(time.now()); + const hasChanged = this.playbackSpeed !== newSpeed; + this.playbackSpeed = newSpeed; + if (hasChanged) { + this.time = millisecondsToSeconds(this.currentTime); + } + } + play() { + if (this.isStopped) + return; + const { driver = frameloopDriver, startTime } = this.options; + if (!this.driver) { + this.driver = driver((timestamp) => this.tick(timestamp)); + } + this.options.onPlay?.(); + const now = this.driver.now(); + if (this.state === "finished") { + this.updateFinished(); + this.startTime = now; + } + else if (this.holdTime !== null) { + this.startTime = now - this.holdTime; + } + else if (!this.startTime) { + this.startTime = startTime ?? now; + } + if (this.state === "finished" && this.speed < 0) { + this.startTime += this.calculatedDuration; + } + this.holdTime = null; + /** + * Set playState to running only after we've used it in + * the previous logic. + */ + this.state = "running"; + this.driver.start(); + } + pause() { + this.state = "paused"; + this.updateTime(time.now()); + this.holdTime = this.currentTime; + } + complete() { + if (this.state !== "running") { + this.play(); + } + this.state = "finished"; + this.holdTime = null; + } + finish() { + this.notifyFinished(); + this.teardown(); + this.state = "finished"; + this.options.onComplete?.(); + } + cancel() { + this.holdTime = null; + this.startTime = 0; + this.tick(0); + this.teardown(); + this.options.onCancel?.(); + } + teardown() { + this.state = "idle"; + this.stopDriver(); + this.startTime = this.holdTime = null; + activeAnimations.mainThread--; + } + stopDriver() { + if (!this.driver) + return; + this.driver.stop(); + this.driver = undefined; + } + sample(sampleTime) { + this.startTime = 0; + return this.tick(sampleTime, true); + } + attachTimeline(timeline) { + if (this.options.allowFlatten) { + this.options.type = "keyframes"; + this.options.ease = "linear"; + this.initAnimation(); + } + this.driver?.stop(); + return timeline.observe(this); + } + } + // Legacy function support + function animateValue(options) { + return new JSAnimation(options); + } + + function fillWildcards(keyframes) { + for (let i = 1; i < keyframes.length; i++) { + keyframes[i] ?? (keyframes[i] = keyframes[i - 1]); + } + } + + const radToDeg = (rad) => (rad * 180) / Math.PI; + const rotate = (v) => { + const angle = radToDeg(Math.atan2(v[1], v[0])); + return rebaseAngle(angle); + }; + const matrix2dParsers = { + x: 4, + y: 5, + translateX: 4, + translateY: 5, + scaleX: 0, + scaleY: 3, + scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2, + rotate, + rotateZ: rotate, + skewX: (v) => radToDeg(Math.atan(v[1])), + skewY: (v) => radToDeg(Math.atan(v[2])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2, + }; + const rebaseAngle = (angle) => { + angle = angle % 360; + if (angle < 0) + angle += 360; + return angle; + }; + const rotateZ = rotate; + const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]); + const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]); + const matrix3dParsers = { + x: 12, + y: 13, + z: 14, + translateX: 12, + translateY: 13, + translateZ: 14, + scaleX, + scaleY, + scale: (v) => (scaleX(v) + scaleY(v)) / 2, + rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))), + rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))), + rotateZ, + rotate: rotateZ, + skewX: (v) => radToDeg(Math.atan(v[4])), + skewY: (v) => radToDeg(Math.atan(v[1])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2, + }; + function defaultTransformValue(name) { + return name.includes("scale") ? 1 : 0; + } + function parseValueFromTransform(transform, name) { + if (!transform || transform === "none") { + return defaultTransformValue(name); + } + const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u); + let parsers; + let match; + if (matrix3dMatch) { + parsers = matrix3dParsers; + match = matrix3dMatch; + } + else { + const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u); + parsers = matrix2dParsers; + match = matrix2dMatch; + } + if (!match) { + return defaultTransformValue(name); + } + const valueParser = parsers[name]; + const values = match[1].split(",").map(convertTransformToNumber); + return typeof valueParser === "function" + ? valueParser(values) + : values[valueParser]; + } + const readTransformValue = (instance, name) => { + const { transform = "none" } = getComputedStyle(instance); + return parseValueFromTransform(transform, name); + }; + function convertTransformToNumber(value) { + return parseFloat(value.trim()); + } + + /** + * Generate a list of every possible transform key. + */ + const transformPropOrder = [ + "transformPerspective", + "x", + "y", + "z", + "translateX", + "translateY", + "translateZ", + "scale", + "scaleX", + "scaleY", + "rotate", + "rotateX", + "rotateY", + "rotateZ", + "skew", + "skewX", + "skewY", + ]; + /** + * A quick lookup for transform props. + */ + const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))(); + + const isNumOrPxType = (v) => v === number || v === px; + const transformKeys = new Set(["x", "y", "z"]); + const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key)); + function removeNonTranslationalTransform(visualElement) { + const removedTransforms = []; + nonTranslationalTransformKeys.forEach((key) => { + const value = visualElement.getValue(key); + if (value !== undefined) { + removedTransforms.push([key, value.get()]); + value.set(key.startsWith("scale") ? 1 : 0); + } + }); + return removedTransforms; + } + const positionalValues = { + // Dimensions + width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight), + height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom), + top: (_bbox, { top }) => parseFloat(top), + left: (_bbox, { left }) => parseFloat(left), + bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min), + right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min), + // Transform + x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"), + y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"), + }; + // Alias translate longform names + positionalValues.translateX = positionalValues.x; + positionalValues.translateY = positionalValues.y; + + const toResolve = new Set(); + let isScheduled = false; + let anyNeedsMeasurement = false; + let isForced = false; + function measureAllKeyframes() { + if (anyNeedsMeasurement) { + const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement); + const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element)); + const transformsToRestore = new Map(); + /** + * Write pass + * If we're measuring elements we want to remove bounding box-changing transforms. + */ + elementsToMeasure.forEach((element) => { + const removedTransforms = removeNonTranslationalTransform(element); + if (!removedTransforms.length) + return; + transformsToRestore.set(element, removedTransforms); + element.render(); + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureInitialState()); + // Write + elementsToMeasure.forEach((element) => { + element.render(); + const restore = transformsToRestore.get(element); + if (restore) { + restore.forEach(([key, value]) => { + element.getValue(key)?.set(value); + }); + } + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureEndState()); + // Write + resolversToMeasure.forEach((resolver) => { + if (resolver.suspendedScrollY !== undefined) { + window.scrollTo(0, resolver.suspendedScrollY); + } + }); + } + anyNeedsMeasurement = false; + isScheduled = false; + toResolve.forEach((resolver) => resolver.complete(isForced)); + toResolve.clear(); + } + function readAllKeyframes() { + toResolve.forEach((resolver) => { + resolver.readKeyframes(); + if (resolver.needsMeasurement) { + anyNeedsMeasurement = true; + } + }); + } + function flushKeyframeResolvers() { + isForced = true; + readAllKeyframes(); + measureAllKeyframes(); + isForced = false; + } + class KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) { + this.state = "pending"; + /** + * Track whether this resolver is async. If it is, it'll be added to the + * resolver queue and flushed in the next frame. Resolvers that aren't going + * to trigger read/write thrashing don't need to be async. + */ + this.isAsync = false; + /** + * Track whether this resolver needs to perform a measurement + * to resolve its keyframes. + */ + this.needsMeasurement = false; + this.unresolvedKeyframes = [...unresolvedKeyframes]; + this.onComplete = onComplete; + this.name = name; + this.motionValue = motionValue; + this.element = element; + this.isAsync = isAsync; + } + scheduleResolve() { + this.state = "scheduled"; + if (this.isAsync) { + toResolve.add(this); + if (!isScheduled) { + isScheduled = true; + frame.read(readAllKeyframes); + frame.resolveKeyframes(measureAllKeyframes); + } + } + else { + this.readKeyframes(); + this.complete(); + } + } + readKeyframes() { + const { unresolvedKeyframes, name, element, motionValue } = this; + // If initial keyframe is null we need to read it from the DOM + if (unresolvedKeyframes[0] === null) { + const currentValue = motionValue?.get(); + // TODO: This doesn't work if the final keyframe is a wildcard + const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (currentValue !== undefined) { + unresolvedKeyframes[0] = currentValue; + } + else if (element && name) { + const valueAsRead = element.readValue(name, finalKeyframe); + if (valueAsRead !== undefined && valueAsRead !== null) { + unresolvedKeyframes[0] = valueAsRead; + } + } + if (unresolvedKeyframes[0] === undefined) { + unresolvedKeyframes[0] = finalKeyframe; + } + if (motionValue && currentValue === undefined) { + motionValue.set(unresolvedKeyframes[0]); + } + } + fillWildcards(unresolvedKeyframes); + } + setFinalKeyframe() { } + measureInitialState() { } + renderEndStyles() { } + measureEndState() { } + complete(isForcedComplete = false) { + this.state = "complete"; + this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForcedComplete); + toResolve.delete(this); + } + cancel() { + if (this.state === "scheduled") { + toResolve.delete(this); + this.state = "pending"; + } + } + resume() { + if (this.state === "pending") + this.scheduleResolve(); + } + } + + const isCSSVar = (name) => name.startsWith("--"); + + function setStyle(element, name, value) { + isCSSVar(name) + ? element.style.setProperty(name, value) + : (element.style[name] = value); + } + + const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined); + + /** + * Add the ability for test suites to manually set support flags + * to better test more environments. + */ + const supportsFlags = {}; + + function memoSupports(callback, supportsFlag) { + const memoized = memo(callback); + return () => supportsFlags[supportsFlag] ?? memoized(); + } + + const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => { + try { + document + .createElement("div") + .animate({ opacity: 0 }, { easing: "linear(0, 1)" }); + } + catch (e) { + return false; + } + return true; + }, "linearEasing"); + + const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`; + + const supportedWaapiEasing = { + linear: "linear", + ease: "ease", + easeIn: "ease-in", + easeOut: "ease-out", + easeInOut: "ease-in-out", + circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]), + circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]), + backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]), + backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]), + }; + + function mapEasingToNativeEasing(easing, duration) { + if (!easing) { + return undefined; + } + else if (typeof easing === "function") { + return supportsLinearEasing() + ? generateLinearEasing(easing, duration) + : "ease-out"; + } + else if (isBezierDefinition(easing)) { + return cubicBezierAsString(easing); + } + else if (Array.isArray(easing)) { + return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) || + supportedWaapiEasing.easeOut); + } + else { + return supportedWaapiEasing[easing]; + } + } + + function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) { + const keyframeOptions = { + [valueName]: keyframes, + }; + if (times) + keyframeOptions.offset = times; + const easing = mapEasingToNativeEasing(ease, duration); + /** + * If this is an easing array, apply to keyframes, not animation as a whole + */ + if (Array.isArray(easing)) + keyframeOptions.easing = easing; + if (statsBuffer.value) { + activeAnimations.waapi++; + } + const options = { + delay, + duration, + easing: !Array.isArray(easing) ? easing : "linear", + fill: "both", + iterations: repeat + 1, + direction: repeatType === "reverse" ? "alternate" : "normal", + }; + if (pseudoElement) + options.pseudoElement = pseudoElement; + const animation = element.animate(keyframeOptions, options); + if (statsBuffer.value) { + animation.finished.finally(() => { + activeAnimations.waapi--; + }); + } + return animation; + } + + function isGenerator(type) { + return typeof type === "function" && "applyToOptions" in type; + } + + function applyGeneratorOptions({ type, ...options }) { + if (isGenerator(type) && supportsLinearEasing()) { + return type.applyToOptions(options); + } + else { + options.duration ?? (options.duration = 300); + options.ease ?? (options.ease = "easeOut"); + } + return options; + } + + /** + * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API. + */ + class NativeAnimation extends WithPromise { + constructor(options) { + super(); + this.finishedTime = null; + this.isStopped = false; + if (!options) + return; + const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options; + this.isPseudoElement = Boolean(pseudoElement); + this.allowFlatten = allowFlatten; + this.options = options; + exports.invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`); + const transition = applyGeneratorOptions(options); + this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement); + if (transition.autoplay === false) { + this.animation.pause(); + } + this.animation.onfinish = () => { + this.finishedTime = this.time; + if (!pseudoElement) { + const keyframe = getFinalKeyframe$1(keyframes, this.options, finalKeyframe, this.speed); + if (this.updateMotionValue) { + this.updateMotionValue(keyframe); + } + else { + /** + * If we can, we want to commit the final style as set by the user, + * rather than the computed keyframe value supplied by the animation. + */ + setStyle(element, name, keyframe); + } + this.animation.cancel(); + } + onComplete?.(); + this.notifyFinished(); + }; + } + play() { + if (this.isStopped) + return; + this.animation.play(); + if (this.state === "finished") { + this.updateFinished(); + } + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.finish?.(); + } + cancel() { + try { + this.animation.cancel(); + } + catch (e) { } + } + stop() { + if (this.isStopped) + return; + this.isStopped = true; + const { state } = this; + if (state === "idle" || state === "finished") { + return; + } + if (this.updateMotionValue) { + this.updateMotionValue(); + } + else { + this.commitStyles(); + } + if (!this.isPseudoElement) + this.cancel(); + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * In this method, we commit styles back to the DOM before cancelling + * the animation. + * + * This is designed to be overridden by NativeAnimationExtended, which + * will create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to also correctly calculate velocity for any subsequent animation + * while deferring the commit until the next animation frame. + */ + commitStyles() { + if (!this.isPseudoElement) { + this.animation.commitStyles?.(); + } + } + get duration() { + const duration = this.animation.effect?.getComputedTiming?.().duration || 0; + return millisecondsToSeconds(Number(duration)); + } + get time() { + return millisecondsToSeconds(Number(this.animation.currentTime) || 0); + } + set time(newTime) { + this.finishedTime = null; + this.animation.currentTime = secondsToMilliseconds(newTime); + } + /** + * The playback speed of the animation. + * 1 = normal speed, 2 = double speed, 0.5 = half speed. + */ + get speed() { + return this.animation.playbackRate; + } + set speed(newSpeed) { + // Allow backwards playback after finishing + if (newSpeed < 0) + this.finishedTime = null; + this.animation.playbackRate = newSpeed; + } + get state() { + return this.finishedTime !== null + ? "finished" + : this.animation.playState; + } + get startTime() { + return Number(this.animation.startTime); + } + set startTime(newStartTime) { + this.animation.startTime = newStartTime; + } + /** + * Attaches a timeline to the animation, for instance the `ScrollTimeline`. + */ + attachTimeline({ timeline, observe }) { + if (this.allowFlatten) { + this.animation.effect?.updateTiming({ easing: "linear" }); + } + this.animation.onfinish = null; + if (timeline && supportsScrollTimeline()) { + this.animation.timeline = timeline; + return noop; + } + else { + return observe(this); + } + } + } + + const unsupportedEasingFunctions = { + anticipate, + backInOut, + circInOut, + }; + function isUnsupportedEase(key) { + return key in unsupportedEasingFunctions; + } + function replaceStringEasing(transition) { + if (typeof transition.ease === "string" && + isUnsupportedEase(transition.ease)) { + transition.ease = unsupportedEasingFunctions[transition.ease]; + } + } + + /** + * 10ms is chosen here as it strikes a balance between smooth + * results (more than one keyframe per frame at 60fps) and + * keyframe quantity. + */ + const sampleDelta = 10; //ms + class NativeAnimationExtended extends NativeAnimation { + constructor(options) { + /** + * The base NativeAnimation function only supports a subset + * of Motion easings, and WAAPI also only supports some + * easing functions via string/cubic-bezier definitions. + * + * This function replaces those unsupported easing functions + * with a JS easing function. This will later get compiled + * to a linear() easing function. + */ + replaceStringEasing(options); + /** + * Ensure we replace the transition type with a generator function + * before passing to WAAPI. + * + * TODO: Does this have a better home? It could be shared with + * JSAnimation. + */ + replaceTransitionType(options); + super(options); + if (options.startTime) { + this.startTime = options.startTime; + } + this.options = options; + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * Rather than read commited styles back out of the DOM, we can + * create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to calculate velocity for any subsequent animation. + */ + updateMotionValue(value) { + const { motionValue, onUpdate, onComplete, element, ...options } = this.options; + if (!motionValue) + return; + if (value !== undefined) { + motionValue.set(value); + return; + } + const sampleAnimation = new JSAnimation({ + ...options, + autoplay: false, + }); + const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time); + motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta); + sampleAnimation.stop(); + } + } + + /** + * Check if a value is animatable. Examples: + * + * ✅: 100, "100px", "#fff" + * ❌: "block", "url(2.jpg)" + * @param value + * + * @internal + */ + const isAnimatable = (value, name) => { + // If the list of keys tat might be non-animatable grows, replace with Set + if (name === "zIndex") + return false; + // If it's a number or a keyframes array, we can animate it. We might at some point + // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this, + // but for now lets leave it like this for performance reasons + if (typeof value === "number" || Array.isArray(value)) + return true; + if (typeof value === "string" && // It's animatable if we have a string + (complex.test(value) || value === "0") && // And it contains numbers and/or colors + !value.startsWith("url(") // Unless it starts with "url(" + ) { + return true; + } + return false; + }; + + function hasKeyframesChanged(keyframes) { + const current = keyframes[0]; + if (keyframes.length === 1) + return true; + for (let i = 0; i < keyframes.length; i++) { + if (keyframes[i] !== current) + return true; + } + } + function canAnimate(keyframes, name, type, velocity) { + /** + * Check if we're able to animate between the start and end keyframes, + * and throw a warning if we're attempting to animate between one that's + * animatable and another that isn't. + */ + const originKeyframe = keyframes[0]; + if (originKeyframe === null) + return false; + /** + * These aren't traditionally animatable but we do support them. + * In future we could look into making this more generic or replacing + * this function with mix() === mixImmediate + */ + if (name === "display" || name === "visibility") + return true; + const targetKeyframe = keyframes[keyframes.length - 1]; + const isOriginAnimatable = isAnimatable(originKeyframe, name); + const isTargetAnimatable = isAnimatable(targetKeyframe, name); + exports.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`); + // Always skip if any of these are true + if (!isOriginAnimatable || !isTargetAnimatable) { + return false; + } + return (hasKeyframesChanged(keyframes) || + ((type === "spring" || isGenerator(type)) && velocity)); + } + + /** + * Checks if an element is an HTML element in a way + * that works across iframes + */ + function isHTMLElement(element) { + return isObject(element) && "offsetHeight" in element; + } + + /** + * A list of values that can be hardware-accelerated. + */ + const acceleratedValues$1 = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Could be re-enabled now we have support for linear() easing + // "background-color" + ]); + const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate")); + function supportsBrowserAnimation(options) { + const { motionValue, name, repeatDelay, repeatType, damping, type } = options; + if (!isHTMLElement(motionValue?.owner?.current)) { + return false; + } + const { onUpdate, transformTemplate } = motionValue.owner.getProps(); + return (supportsWaapi() && + name && + acceleratedValues$1.has(name) && + (name !== "transform" || !transformTemplate) && + /** + * If we're outputting values to onUpdate then we can't use WAAPI as there's + * no way to read the value from WAAPI every frame. + */ + !onUpdate && + !repeatDelay && + repeatType !== "mirror" && + damping !== 0 && + type !== "inertia"); + } + + /** + * Maximum time allowed between an animation being created and it being + * resolved for us to use the latter as the start time. + * + * This is to ensure that while we prefer to "start" an animation as soon + * as it's triggered, we also want to avoid a visual jump if there's a big delay + * between these two moments. + */ + const MAX_RESOLVE_DELAY = 40; + class AsyncMotionValueAnimation extends WithPromise { + constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) { + super(); + /** + * Bound to support return animation.stop pattern + */ + this.stop = () => { + if (this._animation) { + this._animation.stop(); + this.stopTimeline?.(); + } + this.keyframeResolver?.cancel(); + }; + this.createdAt = time.now(); + const optionsWithDefaults = { + autoplay, + delay, + type, + repeat, + repeatDelay, + repeatType, + name, + motionValue, + element, + ...options, + }; + const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver; + this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element); + this.keyframeResolver?.scheduleResolve(); + } + onKeyframesResolved(keyframes, finalKeyframe, options, sync) { + this.keyframeResolver = undefined; + const { name, type, velocity, delay, isHandoff, onUpdate } = options; + this.resolvedAt = time.now(); + /** + * If we can't animate this value with the resolved keyframes + * then we should complete it immediately. + */ + if (!canAnimate(keyframes, name, type, velocity)) { + if (MotionGlobalConfig.instantAnimations || !delay) { + onUpdate?.(getFinalKeyframe$1(keyframes, options, finalKeyframe)); + } + keyframes[0] = keyframes[keyframes.length - 1]; + options.duration = 0; + options.repeat = 0; + } + /** + * Resolve startTime for the animation. + * + * This method uses the createdAt and resolvedAt to calculate the + * animation startTime. *Ideally*, we would use the createdAt time as t=0 + * as the following frame would then be the first frame of the animation in + * progress, which would feel snappier. + * + * However, if there's a delay (main thread work) between the creation of + * the animation and the first commited frame, we prefer to use resolvedAt + * to avoid a sudden jump into the animation. + */ + const startTime = sync + ? !this.resolvedAt + ? this.createdAt + : this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY + ? this.resolvedAt + : this.createdAt + : undefined; + const resolvedOptions = { + startTime, + finalKeyframe, + ...options, + keyframes, + }; + /** + * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via + * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the + * optimised animation. + */ + const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions) + ? new NativeAnimationExtended({ + ...resolvedOptions, + element: resolvedOptions.motionValue.owner.current, + }) + : new JSAnimation(resolvedOptions); + animation.finished.then(() => this.notifyFinished()).catch(noop); + if (this.pendingTimeline) { + this.stopTimeline = animation.attachTimeline(this.pendingTimeline); + this.pendingTimeline = undefined; + } + this._animation = animation; + } + get finished() { + if (!this._animation) { + return this._finished; + } + else { + return this.animation.finished; + } + } + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } + get animation() { + if (!this._animation) { + this.keyframeResolver?.resume(); + flushKeyframeResolvers(); + } + return this._animation; + } + get duration() { + return this.animation.duration; + } + get time() { + return this.animation.time; + } + set time(newTime) { + this.animation.time = newTime; + } + get speed() { + return this.animation.speed; + } + get state() { + return this.animation.state; + } + set speed(newSpeed) { + this.animation.speed = newSpeed; + } + get startTime() { + return this.animation.startTime; + } + attachTimeline(timeline) { + if (this._animation) { + this.stopTimeline = this.animation.attachTimeline(timeline); + } + else { + this.pendingTimeline = timeline; + } + return () => this.stop(); + } + play() { + this.animation.play(); + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.complete(); + } + cancel() { + if (this._animation) { + this.animation.cancel(); + } + this.keyframeResolver?.cancel(); + } + } + + class GroupAnimation { + constructor(animations) { + // Bound to accomadate common `return animation.stop` pattern + this.stop = () => this.runAll("stop"); + this.animations = animations.filter(Boolean); + } + get finished() { + return Promise.all(this.animations.map((animation) => animation.finished)); + } + /** + * TODO: Filter out cancelled or stopped animations before returning + */ + getAll(propName) { + return this.animations[0][propName]; + } + setAll(propName, newValue) { + for (let i = 0; i < this.animations.length; i++) { + this.animations[i][propName] = newValue; + } + } + attachTimeline(timeline) { + const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline)); + return () => { + subscriptions.forEach((cancel, i) => { + cancel && cancel(); + this.animations[i].stop(); + }); + }; + } + get time() { + return this.getAll("time"); + } + set time(time) { + this.setAll("time", time); + } + get speed() { + return this.getAll("speed"); + } + set speed(speed) { + this.setAll("speed", speed); + } + get state() { + return this.getAll("state"); + } + get startTime() { + return this.getAll("startTime"); + } + get duration() { + let max = 0; + for (let i = 0; i < this.animations.length; i++) { + max = Math.max(max, this.animations[i].duration); + } + return max; + } + runAll(methodName) { + this.animations.forEach((controls) => controls[methodName]()); + } + play() { + this.runAll("play"); + } + pause() { + this.runAll("pause"); + } + cancel() { + this.runAll("cancel"); + } + complete() { + this.runAll("complete"); + } + } + + class GroupAnimationWithThen extends GroupAnimation { + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } + } + + class NativeAnimationWrapper extends NativeAnimation { + constructor(animation) { + super(); + this.animation = animation; + animation.onfinish = () => { + this.finishedTime = this.time; + this.notifyFinished(); + }; + } + } + + const animationMaps = new WeakMap(); + const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`; + function getAnimationMap(element) { + const map = animationMaps.get(element) || new Map(); + animationMaps.set(element, map); + return map; + } + + /** + * Parse Framer's special CSS variable format into a CSS token and a fallback. + * + * ``` + * `var(--foo, #fff)` => [`--foo`, '#fff'] + * ``` + * + * @param current + */ + const splitCSSVariableRegex = + // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words + /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u; + function parseCSSVariable(current) { + const match = splitCSSVariableRegex.exec(current); + if (!match) + return [,]; + const [, token1, token2, fallback] = match; + return [`--${token1 ?? token2}`, fallback]; + } + const maxDepth = 4; + function getVariableValue(current, element, depth = 1) { + exports.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`); + const [token, fallback] = parseCSSVariable(current); + // No CSS variable detected + if (!token) + return; + // Attempt to read this CSS variable off the element + const resolved = window.getComputedStyle(element).getPropertyValue(token); + if (resolved) { + const trimmed = resolved.trim(); + return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed; + } + return isCSSVariableToken(fallback) + ? getVariableValue(fallback, element, depth + 1) + : fallback; + } + + function getValueTransition$1(transition, key) { + return (transition?.[key] ?? + transition?.["default"] ?? + transition); + } + + const positionalKeys = new Set([ + "width", + "height", + "top", + "left", + "right", + "bottom", + ...transformPropOrder, + ]); + + /** + * ValueType for "auto" + */ + const auto = { + test: (v) => v === "auto", + parse: (v) => v, + }; + + /** + * Tests a provided value against a ValueType + */ + const testValueType = (v) => (type) => type.test(v); + + /** + * A list of value types commonly used for dimensions + */ + const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto]; + /** + * Tests a dimensional value against the list of dimension ValueTypes + */ + const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v)); + + function isNone(value) { + if (typeof value === "number") { + return value === 0; + } + else if (value !== null) { + return value === "none" || value === "0" || isZeroValueString(value); + } + else { + return true; + } + } + + /** + * Properties that should default to 1 or 100% + */ + const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]); + function applyDefaultFilter(v) { + const [name, value] = v.slice(0, -1).split("("); + if (name === "drop-shadow") + return v; + const [number] = value.match(floatRegex) || []; + if (!number) + return v; + const unit = value.replace(number, ""); + let defaultValue = maxDefaults.has(name) ? 1 : 0; + if (number !== value) + defaultValue *= 100; + return name + "(" + defaultValue + unit + ")"; + } + const functionRegex = /\b([a-z-]*)\(.*?\)/gu; + const filter = { + ...complex, + getAnimatableNone: (v) => { + const functions = v.match(functionRegex); + return functions ? functions.map(applyDefaultFilter).join(" ") : v; + }, + }; + + const int = { + ...number, + transform: Math.round, + }; + + const transformValueTypes = { + rotate: degrees, + rotateX: degrees, + rotateY: degrees, + rotateZ: degrees, + scale, + scaleX: scale, + scaleY: scale, + scaleZ: scale, + skew: degrees, + skewX: degrees, + skewY: degrees, + distance: px, + translateX: px, + translateY: px, + translateZ: px, + x: px, + y: px, + z: px, + perspective: px, + transformPerspective: px, + opacity: alpha, + originX: progressPercentage, + originY: progressPercentage, + originZ: px, + }; + + const numberValueTypes = { + // Border props + borderWidth: px, + borderTopWidth: px, + borderRightWidth: px, + borderBottomWidth: px, + borderLeftWidth: px, + borderRadius: px, + radius: px, + borderTopLeftRadius: px, + borderTopRightRadius: px, + borderBottomRightRadius: px, + borderBottomLeftRadius: px, + // Positioning props + width: px, + maxWidth: px, + height: px, + maxHeight: px, + top: px, + right: px, + bottom: px, + left: px, + // Spacing props + padding: px, + paddingTop: px, + paddingRight: px, + paddingBottom: px, + paddingLeft: px, + margin: px, + marginTop: px, + marginRight: px, + marginBottom: px, + marginLeft: px, + // Misc + backgroundPositionX: px, + backgroundPositionY: px, + ...transformValueTypes, + zIndex: int, + // SVG + fillOpacity: alpha, + strokeOpacity: alpha, + numOctaves: int, + }; + + /** + * A map of default value types for common values + */ + const defaultValueTypes = { + ...numberValueTypes, + // Color props + color, + backgroundColor: color, + outlineColor: color, + fill: color, + stroke: color, + // Border props + borderColor: color, + borderTopColor: color, + borderRightColor: color, + borderBottomColor: color, + borderLeftColor: color, + filter, + WebkitFilter: filter, + }; + /** + * Gets the default ValueType for the provided value key + */ + const getDefaultValueType = (key) => defaultValueTypes[key]; + + function getAnimatableNone(key, value) { + let defaultValueType = getDefaultValueType(key); + if (defaultValueType !== filter) + defaultValueType = complex; + // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target + return defaultValueType.getAnimatableNone + ? defaultValueType.getAnimatableNone(value) + : undefined; + } + + /** + * If we encounter keyframes like "none" or "0" and we also have keyframes like + * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for + * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into + * zero equivalents, i.e. "#fff0" or "0px 0px". + */ + const invalidTemplates = new Set(["auto", "none", "0"]); + function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) { + let i = 0; + let animatableTemplate = undefined; + while (i < unresolvedKeyframes.length && !animatableTemplate) { + const keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string" && + !invalidTemplates.has(keyframe) && + analyseComplexValue(keyframe).values.length) { + animatableTemplate = unresolvedKeyframes[i]; + } + i++; + } + if (animatableTemplate && name) { + for (const noneIndex of noneKeyframeIndexes) { + unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate); + } + } + } + + class DOMKeyframesResolver extends KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element) { + super(unresolvedKeyframes, onComplete, name, motionValue, element, true); + } + readKeyframes() { + const { unresolvedKeyframes, element, name } = this; + if (!element || !element.current) + return; + super.readKeyframes(); + /** + * If any keyframe is a CSS variable, we need to find its value by sampling the element + */ + for (let i = 0; i < unresolvedKeyframes.length; i++) { + let keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string") { + keyframe = keyframe.trim(); + if (isCSSVariableToken(keyframe)) { + const resolved = getVariableValue(keyframe, element.current); + if (resolved !== undefined) { + unresolvedKeyframes[i] = resolved; + } + if (i === unresolvedKeyframes.length - 1) { + this.finalKeyframe = keyframe; + } + } + } + } + /** + * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes. + * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which + * have a far bigger performance impact. + */ + this.resolveNoneKeyframes(); + /** + * Check to see if unit type has changed. If so schedule jobs that will + * temporarily set styles to the destination keyframes. + * Skip if we have more than two keyframes or this isn't a positional value. + * TODO: We can throw if there are multiple keyframes and the value type changes. + */ + if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) { + return; + } + const [origin, target] = unresolvedKeyframes; + const originType = findDimensionValueType(origin); + const targetType = findDimensionValueType(target); + /** + * Either we don't recognise these value types or we can animate between them. + */ + if (originType === targetType) + return; + /** + * If both values are numbers or pixels, we can animate between them by + * converting them to numbers. + */ + if (isNumOrPxType(originType) && isNumOrPxType(targetType)) { + for (let i = 0; i < unresolvedKeyframes.length; i++) { + const value = unresolvedKeyframes[i]; + if (typeof value === "string") { + unresolvedKeyframes[i] = parseFloat(value); + } + } + } + else if (positionalValues[name]) { + /** + * Else, the only way to resolve this is by measuring the element. + */ + this.needsMeasurement = true; + } + } + resolveNoneKeyframes() { + const { unresolvedKeyframes, name } = this; + const noneKeyframeIndexes = []; + for (let i = 0; i < unresolvedKeyframes.length; i++) { + if (unresolvedKeyframes[i] === null || + isNone(unresolvedKeyframes[i])) { + noneKeyframeIndexes.push(i); + } + } + if (noneKeyframeIndexes.length) { + makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name); + } + } + measureInitialState() { + const { element, unresolvedKeyframes, name } = this; + if (!element || !element.current) + return; + if (name === "height") { + this.suspendedScrollY = window.pageYOffset; + } + this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + unresolvedKeyframes[0] = this.measuredOrigin; + // Set final key frame to measure after next render + const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (measureKeyframe !== undefined) { + element.getValue(name, measureKeyframe).jump(measureKeyframe, false); + } + } + measureEndState() { + const { element, name, unresolvedKeyframes } = this; + if (!element || !element.current) + return; + const value = element.getValue(name); + value && value.jump(this.measuredOrigin, false); + const finalKeyframeIndex = unresolvedKeyframes.length - 1; + const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex]; + unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + if (finalKeyframe !== null && this.finalKeyframe === undefined) { + this.finalKeyframe = finalKeyframe; + } + // If we removed transform values, reapply them before the next render + if (this.removedTransforms?.length) { + this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => { + element + .getValue(unsetTransformName) + .set(unsetTransformValue); + }); + } + this.resolveNoneKeyframes(); + } + } + + const pxValues = new Set([ + // Border props + "borderWidth", + "borderTopWidth", + "borderRightWidth", + "borderBottomWidth", + "borderLeftWidth", + "borderRadius", + "radius", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + // Positioning props + "width", + "maxWidth", + "height", + "maxHeight", + "top", + "right", + "bottom", + "left", + // Spacing props + "padding", + "paddingTop", + "paddingRight", + "paddingBottom", + "paddingLeft", + "margin", + "marginTop", + "marginRight", + "marginBottom", + "marginLeft", + // Misc + "backgroundPositionX", + "backgroundPositionY", + ]); + + function applyPxDefaults(keyframes, name) { + for (let i = 0; i < keyframes.length; i++) { + if (typeof keyframes[i] === "number" && pxValues.has(name)) { + keyframes[i] = keyframes[i] + "px"; + } + } + } + + function isWaapiSupportedEasing(easing) { + return Boolean((typeof easing === "function" && supportsLinearEasing()) || + !easing || + (typeof easing === "string" && + (easing in supportedWaapiEasing || supportsLinearEasing())) || + isBezierDefinition(easing) || + (Array.isArray(easing) && easing.every(isWaapiSupportedEasing))); + } + + const supportsPartialKeyframes = /*@__PURE__*/ memo(() => { + try { + document.createElement("div").animate({ opacity: [1] }); + } + catch (e) { + return false; + } + return true; + }); + + /** + * A list of values that can be hardware-accelerated. + */ + const acceleratedValues = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved + // or until we implement support for linear() easing. + // "background-color" + ]); + + function camelToDash$1(str) { + return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`); + } + + function resolveElements(elementOrSelector, scope, selectorCache) { + if (elementOrSelector instanceof EventTarget) { + return [elementOrSelector]; + } + else if (typeof elementOrSelector === "string") { + let root = document; + if (scope) { + root = scope.current; + } + const elements = selectorCache?.[elementOrSelector] ?? + root.querySelectorAll(elementOrSelector); + return elements ? Array.from(elements) : []; + } + return Array.from(elementOrSelector); + } + + function createSelectorEffect(subjectEffect) { + return (subject, values) => { + const elements = resolveElements(subject); + const subscriptions = []; + for (const element of elements) { + const remove = subjectEffect(element, values); + subscriptions.push(remove); + } + return () => { + for (const remove of subscriptions) + remove(); + }; + }; + } + + /** + * Provided a value and a ValueType, returns the value as that value type. + */ + const getValueAsType = (value, type) => { + return type && typeof value === "number" + ? type.transform(value) + : value; + }; + + class MotionValueState { + constructor() { + this.latest = {}; + this.values = new Map(); + } + set(name, value, render, computed, useDefaultValueType = true) { + const existingValue = this.values.get(name); + if (existingValue) { + existingValue.onRemove(); + } + const onChange = () => { + const v = value.get(); + if (useDefaultValueType) { + this.latest[name] = getValueAsType(v, numberValueTypes[name]); + } + else { + this.latest[name] = v; + } + render && frame.render(render); + }; + onChange(); + const cancelOnChange = value.on("change", onChange); + computed && value.addDependent(computed); + const remove = () => { + cancelOnChange(); + render && cancelFrame(render); + this.values.delete(name); + computed && value.removeDependent(computed); + }; + this.values.set(name, { value, onRemove: remove }); + return remove; + } + get(name) { + return this.values.get(name)?.value; + } + destroy() { + for (const value of this.values.values()) { + value.onRemove(); + } + } + } + + function createEffect(addValue) { + const stateCache = new WeakMap(); + const subscriptions = []; + return (subject, values) => { + const state = stateCache.get(subject) ?? new MotionValueState(); + stateCache.set(subject, state); + for (const key in values) { + const value = values[key]; + const remove = addValue(subject, state, key, value); + subscriptions.push(remove); + } + return () => { + for (const cancel of subscriptions) + cancel(); + }; + }; + } + + function canSetAsProperty(element, name) { + if (!(name in element)) + return false; + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), name) || + Object.getOwnPropertyDescriptor(element, name); + // Check if it has a setter + return descriptor && typeof descriptor.set === "function"; + } + const addAttrValue = (element, state, key, value) => { + const isProp = canSetAsProperty(element, key); + const name = isProp + ? key + : key.startsWith("data") || key.startsWith("aria") + ? camelToDash$1(key) + : key; + /** + * Set attribute directly via property if available + */ + const render = isProp + ? () => { + element[name] = state.latest[key]; + } + : () => { + const v = state.latest[key]; + if (v === null || v === undefined) { + element.removeAttribute(name); + } + else { + element.setAttribute(name, String(v)); + } + }; + return state.set(key, value, render); + }; + const attrEffect = /*@__PURE__*/ createSelectorEffect( + /*@__PURE__*/ createEffect(addAttrValue)); + + const propEffect = /*@__PURE__*/ createEffect((subject, state, key, value) => { + return state.set(key, value, () => { + subject[key] = state.latest[key]; + }, undefined, false); + }); + + /** + * Maximum time between the value of two frames, beyond which we + * assume the velocity has since been 0. + */ + const MAX_VELOCITY_DELTA = 30; + const isFloat = (value) => { + return !isNaN(parseFloat(value)); + }; + const collectMotionValues = { + current: undefined, + }; + /** + * `MotionValue` is used to track the state and velocity of motion values. + * + * @public + */ + class MotionValue { + /** + * @param init - The initiating value + * @param config - Optional configuration options + * + * - `transformer`: A function to transform incoming values with. + */ + constructor(init, options = {}) { + /** + * Tracks whether this value can output a velocity. Currently this is only true + * if the value is numerical, but we might be able to widen the scope here and support + * other value types. + * + * @internal + */ + this.canTrackVelocity = null; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + this.updateAndNotify = (v, render = true) => { + const currentTime = time.now(); + /** + * If we're updating the value during another frame or eventloop + * than the previous frame, then the we set the previous frame value + * to current. + */ + if (this.updatedAt !== currentTime) { + this.setPrevFrameValue(); + } + this.prev = this.current; + this.setCurrent(v); + // Update update subscribers + if (this.current !== this.prev) { + this.events.change?.notify(this.current); + if (this.dependents) { + for (const dependent of this.dependents) { + dependent.dirty(); + } + } + } + // Update render subscribers + if (render) { + this.events.renderRequest?.notify(this.current); + } + }; + this.hasAnimated = false; + this.setCurrent(init); + this.owner = options.owner; + } + setCurrent(current) { + this.current = current; + this.updatedAt = time.now(); + if (this.canTrackVelocity === null && current !== undefined) { + this.canTrackVelocity = isFloat(this.current); + } + } + setPrevFrameValue(prevFrameValue = this.current) { + this.prevFrameValue = prevFrameValue; + this.prevUpdatedAt = this.updatedAt; + } + /** + * Adds a function that will be notified when the `MotionValue` is updated. + * + * It returns a function that, when called, will cancel the subscription. + * + * When calling `onChange` inside a React component, it should be wrapped with the + * `useEffect` hook. As it returns an unsubscribe function, this should be returned + * from the `useEffect` function to ensure you don't add duplicate subscribers.. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const y = useMotionValue(0) + * const opacity = useMotionValue(1) + * + * useEffect(() => { + * function updateOpacity() { + * const maxXY = Math.max(x.get(), y.get()) + * const newOpacity = transform(maxXY, [0, 100], [1, 0]) + * opacity.set(newOpacity) + * } + * + * const unsubscribeX = x.on("change", updateOpacity) + * const unsubscribeY = y.on("change", updateOpacity) + * + * return () => { + * unsubscribeX() + * unsubscribeY() + * } + * }, []) + * + * return + * } + * ``` + * + * @param subscriber - A function that receives the latest value. + * @returns A function that, when called, will cancel this subscription. + * + * @deprecated + */ + onChange(subscription) { + { + warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`); + } + return this.on("change", subscription); + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new SubscriptionManager(); + } + const unsubscribe = this.events[eventName].add(callback); + if (eventName === "change") { + return () => { + unsubscribe(); + /** + * If we have no more change listeners by the start + * of the next frame, stop active animations. + */ + frame.read(() => { + if (!this.events.change.getSize()) { + this.stop(); + } + }); + }; + } + return unsubscribe; + } + clearListeners() { + for (const eventManagers in this.events) { + this.events[eventManagers].clear(); + } + } + /** + * Attaches a passive effect to the `MotionValue`. + */ + attach(passiveEffect, stopPassiveEffect) { + this.passiveEffect = passiveEffect; + this.stopPassiveEffect = stopPassiveEffect; + } + /** + * Sets the state of the `MotionValue`. + * + * @remarks + * + * ```jsx + * const x = useMotionValue(0) + * x.set(10) + * ``` + * + * @param latest - Latest value to set. + * @param render - Whether to notify render subscribers. Defaults to `true` + * + * @public + */ + set(v, render = true) { + if (!render || !this.passiveEffect) { + this.updateAndNotify(v, render); + } + else { + this.passiveEffect(v, this.updateAndNotify); + } + } + setWithVelocity(prev, current, delta) { + this.set(current); + this.prev = undefined; + this.prevFrameValue = prev; + this.prevUpdatedAt = this.updatedAt - delta; + } + /** + * Set the state of the `MotionValue`, stopping any active animations, + * effects, and resets velocity to `0`. + */ + jump(v, endAnimation = true) { + this.updateAndNotify(v); + this.prev = v; + this.prevUpdatedAt = this.prevFrameValue = undefined; + endAnimation && this.stop(); + if (this.stopPassiveEffect) + this.stopPassiveEffect(); + } + dirty() { + this.events.change?.notify(this.current); + } + addDependent(dependent) { + if (!this.dependents) { + this.dependents = new Set(); + } + this.dependents.add(dependent); + } + removeDependent(dependent) { + if (this.dependents) { + this.dependents.delete(dependent); + } + } + /** + * Returns the latest state of `MotionValue` + * + * @returns - The latest state of `MotionValue` + * + * @public + */ + get() { + if (collectMotionValues.current) { + collectMotionValues.current.push(this); + } + return this.current; + } + /** + * @public + */ + getPrevious() { + return this.prev; + } + /** + * Returns the latest velocity of `MotionValue` + * + * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. + * + * @public + */ + getVelocity() { + const currentTime = time.now(); + if (!this.canTrackVelocity || + this.prevFrameValue === undefined || + currentTime - this.updatedAt > MAX_VELOCITY_DELTA) { + return 0; + } + const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA); + // Casts because of parseFloat's poor typing + return velocityPerSecond(parseFloat(this.current) - + parseFloat(this.prevFrameValue), delta); + } + /** + * Registers a new animation to control this `MotionValue`. Only one + * animation can drive a `MotionValue` at one time. + * + * ```jsx + * value.start() + * ``` + * + * @param animation - A function that starts the provided animation + */ + start(startAnimation) { + this.stop(); + return new Promise((resolve) => { + this.hasAnimated = true; + this.animation = startAnimation(resolve); + if (this.events.animationStart) { + this.events.animationStart.notify(); + } + }).then(() => { + if (this.events.animationComplete) { + this.events.animationComplete.notify(); + } + this.clearAnimation(); + }); + } + /** + * Stop the currently active animation. + * + * @public + */ + stop() { + if (this.animation) { + this.animation.stop(); + if (this.events.animationCancel) { + this.events.animationCancel.notify(); + } + } + this.clearAnimation(); + } + /** + * Returns `true` if this value is currently animating. + * + * @public + */ + isAnimating() { + return !!this.animation; + } + clearAnimation() { + delete this.animation; + } + /** + * Destroy and clean up subscribers to this `MotionValue`. + * + * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically + * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually + * created a `MotionValue` via the `motionValue` function. + * + * @public + */ + destroy() { + this.dependents?.clear(); + this.events.destroy?.notify(); + this.clearListeners(); + this.stop(); + if (this.stopPassiveEffect) { + this.stopPassiveEffect(); + } + } + } + function motionValue(init, options) { + return new MotionValue(init, options); + } + + const translateAlias$1 = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", + }; + function buildTransform$1(state) { + let transform = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < transformPropOrder.length; i++) { + const key = transformPropOrder[i]; + const value = state.latest[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias$1[key] || key; + const valueToRender = state.latest[key]; + transform += `${transformName}(${valueToRender}) `; + } + } + return transformIsDefault ? "none" : transform.trim(); + } + + const originProps = new Set(["originX", "originY", "originZ"]); + const addStyleValue = (element, state, key, value) => { + let render = undefined; + let computed = undefined; + if (transformProps.has(key)) { + if (!state.get("transform")) { + // If this is an HTML element, we need to set the transform-box to fill-box + // to normalise the transform relative to the element's bounding box + if (!isHTMLElement(element) && !state.get("transformBox")) { + addStyleValue(element, state, "transformBox", new MotionValue("fill-box")); + } + state.set("transform", new MotionValue("none"), () => { + element.style.transform = buildTransform$1(state); + }); + } + computed = state.get("transform"); + } + else if (originProps.has(key)) { + if (!state.get("transformOrigin")) { + state.set("transformOrigin", new MotionValue(""), () => { + const originX = state.latest.originX ?? "50%"; + const originY = state.latest.originY ?? "50%"; + const originZ = state.latest.originZ ?? 0; + element.style.transformOrigin = `${originX} ${originY} ${originZ}`; + }); + } + computed = state.get("transformOrigin"); + } + else if (isCSSVar(key)) { + render = () => { + element.style.setProperty(key, state.latest[key]); + }; + } + else { + render = () => { + element.style[key] = state.latest[key]; + }; + } + return state.set(key, value, render, computed); + }; + const styleEffect = /*@__PURE__*/ createSelectorEffect( + /*@__PURE__*/ createEffect(addStyleValue)); + + const toPx = px.transform; + function addSVGPathValue(element, state, key, value) { + frame.render(() => element.setAttribute("pathLength", "1")); + if (key === "pathOffset") { + return state.set(key, value, () => element.setAttribute("stroke-dashoffset", toPx(-state.latest[key]))); + } + else { + if (!state.get("stroke-dasharray")) { + state.set("stroke-dasharray", new MotionValue("1 1"), () => { + const { pathLength = 1, pathSpacing } = state.latest; + element.setAttribute("stroke-dasharray", `${toPx(pathLength)} ${toPx(pathSpacing ?? 1 - Number(pathLength))}`); + }); + } + return state.set(key, value, undefined, state.get("stroke-dasharray")); + } + } + const addSVGValue = (element, state, key, value) => { + if (key.startsWith("path")) { + return addSVGPathValue(element, state, key, value); + } + else if (key.startsWith("attr")) { + return addAttrValue(element, state, convertAttrKey(key), value); + } + const handler = key in element.style ? addStyleValue : addAttrValue; + return handler(element, state, key, value); + }; + const svgEffect = /*@__PURE__*/ createSelectorEffect( + /*@__PURE__*/ createEffect(addSVGValue)); + function convertAttrKey(key) { + return key.replace(/^attr([A-Z])/, (_, firstChar) => firstChar.toLowerCase()); + } + + const { schedule: microtask, cancel: cancelMicrotask } = + /* @__PURE__ */ createRenderBatcher(queueMicrotask, false); + + const isDragging = { + x: false, + y: false, + }; + function isDragActive() { + return isDragging.x || isDragging.y; + } + + function setDragLock(axis) { + if (axis === "x" || axis === "y") { + if (isDragging[axis]) { + return null; + } + else { + isDragging[axis] = true; + return () => { + isDragging[axis] = false; + }; + } + } + else { + if (isDragging.x || isDragging.y) { + return null; + } + else { + isDragging.x = isDragging.y = true; + return () => { + isDragging.x = isDragging.y = false; + }; + } + } + } + + function setupGesture(elementOrSelector, options) { + const elements = resolveElements(elementOrSelector); + const gestureAbortController = new AbortController(); + const eventOptions = { + passive: true, + ...options, + signal: gestureAbortController.signal, + }; + const cancel = () => gestureAbortController.abort(); + return [elements, eventOptions, cancel]; + } + + function isValidHover(event) { + return !(event.pointerType === "touch" || isDragActive()); + } + /** + * Create a hover gesture. hover() is different to .addEventListener("pointerenter") + * in that it has an easier syntax, filters out polyfilled touch events, interoperates + * with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends. + * + * @public + */ + function hover(elementOrSelector, onHoverStart, options = {}) { + const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options); + const onPointerEnter = (enterEvent) => { + if (!isValidHover(enterEvent)) + return; + const { target } = enterEvent; + const onHoverEnd = onHoverStart(target, enterEvent); + if (typeof onHoverEnd !== "function" || !target) + return; + const onPointerLeave = (leaveEvent) => { + if (!isValidHover(leaveEvent)) + return; + onHoverEnd(leaveEvent); + target.removeEventListener("pointerleave", onPointerLeave); + }; + target.addEventListener("pointerleave", onPointerLeave, eventOptions); + }; + elements.forEach((element) => { + element.addEventListener("pointerenter", onPointerEnter, eventOptions); + }); + return cancel; + } + + /** + * Recursively traverse up the tree to check whether the provided child node + * is the parent or a descendant of it. + * + * @param parent - Element to find + * @param child - Element to test against parent + */ + const isNodeOrChild = (parent, child) => { + if (!child) { + return false; + } + else if (parent === child) { + return true; + } + else { + return isNodeOrChild(parent, child.parentElement); + } + }; + + const isPrimaryPointer = (event) => { + if (event.pointerType === "mouse") { + return typeof event.button !== "number" || event.button <= 0; + } + else { + /** + * isPrimary is true for all mice buttons, whereas every touch point + * is regarded as its own input. So subsequent concurrent touch points + * will be false. + * + * Specifically match against false here as incomplete versions of + * PointerEvents in very old browser might have it set as undefined. + */ + return event.isPrimary !== false; + } + }; + + const focusableElements = new Set([ + "BUTTON", + "INPUT", + "SELECT", + "TEXTAREA", + "A", + ]); + function isElementKeyboardAccessible(element) { + return (focusableElements.has(element.tagName) || + element.tabIndex !== -1); + } + + const isPressing = new WeakSet(); + + /** + * Filter out events that are not "Enter" keys. + */ + function filterEvents(callback) { + return (event) => { + if (event.key !== "Enter") + return; + callback(event); + }; + } + function firePointerEvent(target, type) { + target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true })); + } + const enableKeyboardPress = (focusEvent, eventOptions) => { + const element = focusEvent.currentTarget; + if (!element) + return; + const handleKeydown = filterEvents(() => { + if (isPressing.has(element)) + return; + firePointerEvent(element, "down"); + const handleKeyup = filterEvents(() => { + firePointerEvent(element, "up"); + }); + const handleBlur = () => firePointerEvent(element, "cancel"); + element.addEventListener("keyup", handleKeyup, eventOptions); + element.addEventListener("blur", handleBlur, eventOptions); + }); + element.addEventListener("keydown", handleKeydown, eventOptions); + /** + * Add an event listener that fires on blur to remove the keydown events. + */ + element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions); + }; + + /** + * Filter out events that are not primary pointer events, or are triggering + * while a Motion gesture is active. + */ + function isValidPressEvent(event) { + return isPrimaryPointer(event) && !isDragActive(); + } + /** + * Create a press gesture. + * + * Press is different to `"pointerdown"`, `"pointerup"` in that it + * automatically filters out secondary pointer events like right + * click and multitouch. + * + * It also adds accessibility support for keyboards, where + * an element with a press gesture will receive focus and + * trigger on Enter `"keydown"` and `"keyup"` events. + * + * This is different to a browser's `"click"` event, which does + * respond to keyboards but only for the `"click"` itself, rather + * than the press start and end/cancel. The element also needs + * to be focusable for this to work, whereas a press gesture will + * make an element focusable by default. + * + * @public + */ + function press(targetOrSelector, onPressStart, options = {}) { + const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options); + const startPress = (startEvent) => { + const target = startEvent.currentTarget; + if (!isValidPressEvent(startEvent)) + return; + isPressing.add(target); + const onPressEnd = onPressStart(target, startEvent); + const onPointerEnd = (endEvent, success) => { + window.removeEventListener("pointerup", onPointerUp); + window.removeEventListener("pointercancel", onPointerCancel); + if (isPressing.has(target)) { + isPressing.delete(target); + } + if (!isValidPressEvent(endEvent)) { + return; + } + if (typeof onPressEnd === "function") { + onPressEnd(endEvent, { success }); + } + }; + const onPointerUp = (upEvent) => { + onPointerEnd(upEvent, target === window || + target === document || + options.useGlobalTarget || + isNodeOrChild(target, upEvent.target)); + }; + const onPointerCancel = (cancelEvent) => { + onPointerEnd(cancelEvent, false); + }; + window.addEventListener("pointerup", onPointerUp, eventOptions); + window.addEventListener("pointercancel", onPointerCancel, eventOptions); + }; + targets.forEach((target) => { + const pointerDownTarget = options.useGlobalTarget ? window : target; + pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions); + if (isHTMLElement(target)) { + target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions)); + if (!isElementKeyboardAccessible(target) && + !target.hasAttribute("tabindex")) { + target.tabIndex = 0; + } + } + }); + return cancelEvents; + } + + function getComputedStyle$2(element, name) { + const computedStyle = window.getComputedStyle(element); + return isCSSVar(name) + ? computedStyle.getPropertyValue(name) + : computedStyle[name]; + } + + /** + * Checks if an element is an SVG element in a way + * that works across iframes + */ + function isSVGElement(element) { + return isObject(element) && "ownerSVGElement" in element; + } + + const resizeHandlers = new WeakMap(); + let observer; + const getSize = (borderBoxAxis, svgAxis, htmlAxis) => (target, borderBoxSize) => { + if (borderBoxSize && borderBoxSize[0]) { + return borderBoxSize[0][(borderBoxAxis + "Size")]; + } + else if (isSVGElement(target) && "getBBox" in target) { + return target.getBBox()[svgAxis]; + } + else { + return target[htmlAxis]; + } + }; + const getWidth = /*@__PURE__*/ getSize("inline", "width", "offsetWidth"); + const getHeight = /*@__PURE__*/ getSize("block", "height", "offsetHeight"); + function notifyTarget({ target, borderBoxSize }) { + resizeHandlers.get(target)?.forEach((handler) => { + handler(target, { + get width() { + return getWidth(target, borderBoxSize); + }, + get height() { + return getHeight(target, borderBoxSize); + }, + }); + }); + } + function notifyAll(entries) { + entries.forEach(notifyTarget); + } + function createResizeObserver() { + if (typeof ResizeObserver === "undefined") + return; + observer = new ResizeObserver(notifyAll); + } + function resizeElement(target, handler) { + if (!observer) + createResizeObserver(); + const elements = resolveElements(target); + elements.forEach((element) => { + let elementHandlers = resizeHandlers.get(element); + if (!elementHandlers) { + elementHandlers = new Set(); + resizeHandlers.set(element, elementHandlers); + } + elementHandlers.add(handler); + observer?.observe(element); + }); + return () => { + elements.forEach((element) => { + const elementHandlers = resizeHandlers.get(element); + elementHandlers?.delete(handler); + if (!elementHandlers?.size) { + observer?.unobserve(element); + } + }); + }; + } + + const windowCallbacks = new Set(); + let windowResizeHandler; + function createWindowResizeHandler() { + windowResizeHandler = () => { + const info = { + get width() { + return window.innerWidth; + }, + get height() { + return window.innerHeight; + }, + }; + windowCallbacks.forEach((callback) => callback(info)); + }; + window.addEventListener("resize", windowResizeHandler); + } + function resizeWindow(callback) { + windowCallbacks.add(callback); + if (!windowResizeHandler) + createWindowResizeHandler(); + return () => { + windowCallbacks.delete(callback); + if (!windowCallbacks.size && + typeof windowResizeHandler === "function") { + window.removeEventListener("resize", windowResizeHandler); + windowResizeHandler = undefined; + } + }; + } + + function resize(a, b) { + return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b); + } + + function observeTimeline(update, timeline) { + let prevProgress; + const onFrame = () => { + const { currentTime } = timeline; + const percentage = currentTime === null ? 0 : currentTime.value; + const progress = percentage / 100; + if (prevProgress !== progress) { + update(progress); + } + prevProgress = progress; + }; + frame.preUpdate(onFrame, true); + return () => cancelFrame(onFrame); + } + + function record() { + const { value } = statsBuffer; + if (value === null) { + cancelFrame(record); + return; + } + value.frameloop.rate.push(frameData.delta); + value.animations.mainThread.push(activeAnimations.mainThread); + value.animations.waapi.push(activeAnimations.waapi); + value.animations.layout.push(activeAnimations.layout); + } + function mean(values) { + return values.reduce((acc, value) => acc + value, 0) / values.length; + } + function summarise(values, calcAverage = mean) { + if (values.length === 0) { + return { + min: 0, + max: 0, + avg: 0, + }; + } + return { + min: Math.min(...values), + max: Math.max(...values), + avg: calcAverage(values), + }; + } + const msToFps = (ms) => Math.round(1000 / ms); + function clearStatsBuffer() { + statsBuffer.value = null; + statsBuffer.addProjectionMetrics = null; + } + function reportStats() { + const { value } = statsBuffer; + if (!value) { + throw new Error("Stats are not being measured"); + } + clearStatsBuffer(); + cancelFrame(record); + const summary = { + frameloop: { + setup: summarise(value.frameloop.setup), + rate: summarise(value.frameloop.rate), + read: summarise(value.frameloop.read), + resolveKeyframes: summarise(value.frameloop.resolveKeyframes), + preUpdate: summarise(value.frameloop.preUpdate), + update: summarise(value.frameloop.update), + preRender: summarise(value.frameloop.preRender), + render: summarise(value.frameloop.render), + postRender: summarise(value.frameloop.postRender), + }, + animations: { + mainThread: summarise(value.animations.mainThread), + waapi: summarise(value.animations.waapi), + layout: summarise(value.animations.layout), + }, + layoutProjection: { + nodes: summarise(value.layoutProjection.nodes), + calculatedTargetDeltas: summarise(value.layoutProjection.calculatedTargetDeltas), + calculatedProjections: summarise(value.layoutProjection.calculatedProjections), + }, + }; + /** + * Convert the rate to FPS + */ + const { rate } = summary.frameloop; + rate.min = msToFps(rate.min); + rate.max = msToFps(rate.max); + rate.avg = msToFps(rate.avg); + [rate.min, rate.max] = [rate.max, rate.min]; + return summary; + } + function recordStats() { + if (statsBuffer.value) { + clearStatsBuffer(); + throw new Error("Stats are already being measured"); + } + const newStatsBuffer = statsBuffer; + newStatsBuffer.value = { + frameloop: { + setup: [], + rate: [], + read: [], + resolveKeyframes: [], + preUpdate: [], + update: [], + preRender: [], + render: [], + postRender: [], + }, + animations: { + mainThread: [], + waapi: [], + layout: [], + }, + layoutProjection: { + nodes: [], + calculatedTargetDeltas: [], + calculatedProjections: [], + }, + }; + newStatsBuffer.addProjectionMetrics = (metrics) => { + const { layoutProjection } = newStatsBuffer.value; + layoutProjection.nodes.push(metrics.nodes); + layoutProjection.calculatedTargetDeltas.push(metrics.calculatedTargetDeltas); + layoutProjection.calculatedProjections.push(metrics.calculatedProjections); + }; + frame.postRender(record, true); + return reportStats; + } + + /** + * Checks if an element is specifically an SVGSVGElement (the root SVG element) + * in a way that works across iframes + */ + function isSVGSVGElement(element) { + return isSVGElement(element) && element.tagName === "svg"; + } + + function getOriginIndex(from, total) { + if (from === "first") { + return 0; + } + else { + const lastIndex = total - 1; + return from === "last" ? lastIndex : lastIndex / 2; + } + } + function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) { + return (i, total) => { + const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total); + const distance = Math.abs(fromIndex - i); + let delay = duration * distance; + if (ease) { + const maxDelay = total * duration; + const easingFunction = easingDefinitionToFunction(ease); + delay = easingFunction(delay / maxDelay) * maxDelay; + } + return startDelay + delay; + }; + } + + function transform(...args) { + const useImmediate = !Array.isArray(args[0]); + const argOffset = useImmediate ? 0 : -1; + const inputValue = args[0 + argOffset]; + const inputRange = args[1 + argOffset]; + const outputRange = args[2 + argOffset]; + const options = args[3 + argOffset]; + const interpolator = interpolate(inputRange, outputRange, options); + return useImmediate ? interpolator(inputValue) : interpolator; + } + + function subscribeValue(inputValues, outputValue, getLatest) { + const update = () => outputValue.set(getLatest()); + const scheduleUpdate = () => frame.preRender(update, false, true); + const subscriptions = inputValues.map((v) => v.on("change", scheduleUpdate)); + outputValue.on("destroy", () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + cancelFrame(update); + }); + } + + /** + * Create a `MotionValue` that transforms the output of other `MotionValue`s by + * passing their latest values through a transform function. + * + * Whenever a `MotionValue` referred to in the provided function is updated, + * it will be re-evaluated. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ + function transformValue(transform) { + const collectedValues = []; + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * inside transform will be saved into this array. + */ + collectMotionValues.current = collectedValues; + const initialValue = transform(); + collectMotionValues.current = undefined; + const value = motionValue(initialValue); + subscribeValue(collectedValues, value, transform); + return value; + } + + /** + * Create a `MotionValue` that maps the output of another `MotionValue` by + * mapping it from one range of values into another. + * + * @remarks + * + * Given an input range of `[-200, -100, 100, 200]` and an output range of + * `[0, 1, 1, 0]`, the returned `MotionValue` will: + * + * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`. + * - When provided a value between `-100` and `100`, will return `1`. + * - When provided a value between `100` and `200`, will return a value between `1` and `0` + * + * The input range must be a linear series of numbers. The output range + * can be any value type supported by Motion: numbers, colors, shadows, etc. + * + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * const x = motionValue(0) + * const xRange = [-200, -100, 100, 200] + * const opacityRange = [0, 1, 1, 0] + * const opacity = mapValue(x, xRange, opacityRange) + * ``` + * + * @param inputValue - `MotionValue` + * @param inputRange - A linear series of numbers (either all increasing or decreasing) + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - + * + * - clamp: boolean. Clamp values to within the given range. Defaults to `true` + * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each. + * + * @returns `MotionValue` + * + * @public + */ + function mapValue(inputValue, inputRange, outputRange, options) { + const map = transform(inputRange, outputRange, options); + return transformValue(() => map(inputValue.get())); + } + + const isMotionValue = (value) => Boolean(value && value.getVelocity); + + /** + * Create a `MotionValue` that animates to its latest value using a spring. + * Can either be a value or track another `MotionValue`. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ + function springValue(source, options) { + const initialValue = isMotionValue(source) ? source.get() : source; + const value = motionValue(initialValue); + attachSpring(value, source, options); + return value; + } + function attachSpring(value, source, options) { + const initialValue = value.get(); + let activeAnimation = null; + let latestValue = initialValue; + let latestSetter; + const unit = typeof initialValue === "string" + ? initialValue.replace(/[\d.-]/g, "") + : undefined; + const stopAnimation = () => { + if (activeAnimation) { + activeAnimation.stop(); + activeAnimation = null; + } + }; + const startAnimation = () => { + stopAnimation(); + activeAnimation = new JSAnimation({ + keyframes: [asNumber$1(value.get()), asNumber$1(latestValue)], + velocity: value.getVelocity(), + type: "spring", + restDelta: 0.001, + restSpeed: 0.01, + ...options, + onUpdate: latestSetter, + }); + }; + value.attach((v, set) => { + latestValue = v; + latestSetter = (latest) => set(parseValue(latest, unit)); + frame.postRender(startAnimation); + return value.get(); + }, stopAnimation); + let unsubscribe = undefined; + if (isMotionValue(source)) { + unsubscribe = source.on("change", (v) => value.set(parseValue(v, unit))); + value.on("destroy", unsubscribe); + } + return unsubscribe; + } + function parseValue(v, unit) { + return unit ? v + unit : v; + } + function asNumber$1(v) { + return typeof v === "number" ? v : parseFloat(v); + } + + /** + * A list of all ValueTypes + */ + const valueTypes = [...dimensionValueTypes, color, complex]; + /** + * Tests a value against the list of ValueTypes + */ + const findValueType = (v) => valueTypes.find(testValueType(v)); + + function chooseLayerType(valueName) { + if (valueName === "layout") + return "group"; + if (valueName === "enter" || valueName === "new") + return "new"; + if (valueName === "exit" || valueName === "old") + return "old"; + return "group"; + } + + let pendingRules = {}; + let style = null; + const css = { + set: (selector, values) => { + pendingRules[selector] = values; + }, + commit: () => { + if (!style) { + style = document.createElement("style"); + style.id = "motion-view"; + } + let cssText = ""; + for (const selector in pendingRules) { + const rule = pendingRules[selector]; + cssText += `${selector} {\n`; + for (const [property, value] of Object.entries(rule)) { + cssText += ` ${property}: ${value};\n`; + } + cssText += "}\n"; + } + style.textContent = cssText; + document.head.appendChild(style); + pendingRules = {}; + }, + remove: () => { + if (style && style.parentElement) { + style.parentElement.removeChild(style); + } + }, + }; + + function getLayerName(pseudoElement) { + const match = pseudoElement.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/); + if (!match) + return null; + return { layer: match[2], type: match[1] }; + } + + function filterViewAnimations(animation) { + const { effect } = animation; + if (!effect) + return false; + return (effect.target === document.documentElement && + effect.pseudoElement?.startsWith("::view-transition")); + } + function getViewAnimations() { + return document.getAnimations().filter(filterViewAnimations); + } + + function hasTarget(target, targets) { + return targets.has(target) && Object.keys(targets.get(target)).length > 0; + } + + const definitionNames = ["layout", "enter", "exit", "new", "old"]; + function startViewAnimation(builder) { + const { update, targets, options: defaultOptions } = builder; + if (!document.startViewTransition) { + return new Promise(async (resolve) => { + await update(); + resolve(new GroupAnimation([])); + }); + } + // TODO: Go over existing targets and ensure they all have ids + /** + * If we don't have any animations defined for the root target, + * remove it from being captured. + */ + if (!hasTarget("root", targets)) { + css.set(":root", { + "view-transition-name": "none", + }); + } + /** + * Set the timing curve to linear for all view transition layers. + * This gets baked into the keyframes, which can't be changed + * without breaking the generated animation. + * + * This allows us to set easing via updateTiming - which can be changed. + */ + css.set("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)", { "animation-timing-function": "linear !important" }); + css.commit(); // Write + const transition = document.startViewTransition(async () => { + await update(); + // TODO: Go over new targets and ensure they all have ids + }); + transition.finished.finally(() => { + css.remove(); // Write + }); + return new Promise((resolve) => { + transition.ready.then(() => { + const generatedViewAnimations = getViewAnimations(); + const animations = []; + /** + * Create animations for each of our explicitly-defined subjects. + */ + targets.forEach((definition, target) => { + // TODO: If target is not "root", resolve elements + // and iterate over each + for (const key of definitionNames) { + if (!definition[key]) + continue; + const { keyframes, options } = definition[key]; + for (let [valueName, valueKeyframes] of Object.entries(keyframes)) { + if (!valueKeyframes) + continue; + const valueOptions = { + ...getValueTransition$1(defaultOptions, valueName), + ...getValueTransition$1(options, valueName), + }; + const type = chooseLayerType(key); + /** + * If this is an opacity animation, and keyframes are not an array, + * we need to convert them into an array and set an initial value. + */ + if (valueName === "opacity" && + !Array.isArray(valueKeyframes)) { + const initialValue = type === "new" ? 0 : 1; + valueKeyframes = [initialValue, valueKeyframes]; + } + /** + * Resolve stagger function if provided. + */ + if (typeof valueOptions.delay === "function") { + valueOptions.delay = valueOptions.delay(0, 1); + } + valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay)); + const animation = new NativeAnimation({ + ...valueOptions, + element: document.documentElement, + name: valueName, + pseudoElement: `::view-transition-${type}(${target})`, + keyframes: valueKeyframes, + }); + animations.push(animation); + } + } + }); + /** + * Handle browser generated animations + */ + for (const animation of generatedViewAnimations) { + if (animation.playState === "finished") + continue; + const { effect } = animation; + if (!effect || !(effect instanceof KeyframeEffect)) + continue; + const { pseudoElement } = effect; + if (!pseudoElement) + continue; + const name = getLayerName(pseudoElement); + if (!name) + continue; + const targetDefinition = targets.get(name.layer); + if (!targetDefinition) { + /** + * If transition name is group then update the timing of the animation + * whereas if it's old or new then we could possibly replace it using + * the above method. + */ + const transitionName = name.type === "group" ? "layout" : ""; + let animationTransition = { + ...getValueTransition$1(defaultOptions, transitionName), + }; + animationTransition.duration && (animationTransition.duration = secondsToMilliseconds(animationTransition.duration)); + animationTransition = + applyGeneratorOptions(animationTransition); + const easing = mapEasingToNativeEasing(animationTransition.ease, animationTransition.duration); + effect.updateTiming({ + delay: secondsToMilliseconds(animationTransition.delay ?? 0), + duration: animationTransition.duration, + easing, + }); + animations.push(new NativeAnimationWrapper(animation)); + } + else if (hasOpacity(targetDefinition, "enter") && + hasOpacity(targetDefinition, "exit") && + effect + .getKeyframes() + .some((keyframe) => keyframe.mixBlendMode)) { + animations.push(new NativeAnimationWrapper(animation)); + } + else { + animation.cancel(); + } + } + resolve(new GroupAnimation(animations)); + }); + }); + } + function hasOpacity(target, key) { + return target?.[key]?.keyframes.opacity; + } + + let builders = []; + let current = null; + function next() { + current = null; + const [nextBuilder] = builders; + if (nextBuilder) + start(nextBuilder); + } + function start(builder) { + removeItem(builders, builder); + current = builder; + startViewAnimation(builder).then((animation) => { + builder.notifyReady(animation); + animation.finished.finally(next); + }); + } + function processQueue() { + /** + * Iterate backwards over the builders array. We can ignore the + * "wait" animations. If we have an interrupting animation in the + * queue then we need to batch all preceeding animations into it. + * Currently this only batches the update functions but will also + * need to batch the targets. + */ + for (let i = builders.length - 1; i >= 0; i--) { + const builder = builders[i]; + const { interrupt } = builder.options; + if (interrupt === "immediate") { + const batchedUpdates = builders.slice(0, i + 1).map((b) => b.update); + const remaining = builders.slice(i + 1); + builder.update = () => { + batchedUpdates.forEach((update) => update()); + }; + // Put the current builder at the front, followed by any "wait" builders + builders = [builder, ...remaining]; + break; + } + } + if (!current || builders[0]?.options.interrupt === "immediate") { + next(); + } + } + function addToQueue(builder) { + builders.push(builder); + microtask.render(processQueue); + } + + class ViewTransitionBuilder { + constructor(update, options = {}) { + this.currentSubject = "root"; + this.targets = new Map(); + this.notifyReady = noop; + this.readyPromise = new Promise((resolve) => { + this.notifyReady = resolve; + }); + this.update = update; + this.options = { + interrupt: "wait", + ...options, + }; + addToQueue(this); + } + get(subject) { + this.currentSubject = subject; + return this; + } + layout(keyframes, options) { + this.updateTarget("layout", keyframes, options); + return this; + } + new(keyframes, options) { + this.updateTarget("new", keyframes, options); + return this; + } + old(keyframes, options) { + this.updateTarget("old", keyframes, options); + return this; + } + enter(keyframes, options) { + this.updateTarget("enter", keyframes, options); + return this; + } + exit(keyframes, options) { + this.updateTarget("exit", keyframes, options); + return this; + } + crossfade(options) { + this.updateTarget("enter", { opacity: 1 }, options); + this.updateTarget("exit", { opacity: 0 }, options); + return this; + } + updateTarget(target, keyframes, options = {}) { + const { currentSubject, targets } = this; + if (!targets.has(currentSubject)) { + targets.set(currentSubject, {}); + } + const targetData = targets.get(currentSubject); + targetData[target] = { keyframes, options }; + } + then(resolve, reject) { + return this.readyPromise.then(resolve, reject); + } + } + function animateView(update, defaultOptions = {}) { + return new ViewTransitionBuilder(update, defaultOptions); + } + + /** + * @deprecated + * + * Import as `frame` instead. + */ + const sync = frame; + /** + * @deprecated + * + * Use cancelFrame(callback) instead. + */ + const cancelSync = stepsOrder.reduce((acc, key) => { + acc[key] = (process) => cancelFrame(process); + return acc; + }, {}); + + /** + * @public + */ + const MotionConfigContext = React$1.createContext({ + transformPagePoint: (p) => p, + isStatic: false, + reducedMotion: "never", + }); + + /** + * Measurement functionality has to be within a separate component + * to leverage snapshot lifecycle. + */ + class PopChildMeasure extends React__namespace.Component { + getSnapshotBeforeUpdate(prevProps) { + const element = this.props.childRef.current; + if (element && prevProps.isPresent && !this.props.isPresent) { + const parent = element.offsetParent; + const parentWidth = isHTMLElement(parent) + ? parent.offsetWidth || 0 + : 0; + const size = this.props.sizeRef.current; + size.height = element.offsetHeight || 0; + size.width = element.offsetWidth || 0; + size.top = element.offsetTop; + size.left = element.offsetLeft; + size.right = parentWidth - size.width - size.left; + } + return null; + } + /** + * Required with getSnapshotBeforeUpdate to stop React complaining. + */ + componentDidUpdate() { } + render() { + return this.props.children; + } + } + function PopChild({ children, isPresent, anchorX, root }) { + const id = React$1.useId(); + const ref = React$1.useRef(null); + const size = React$1.useRef({ + width: 0, + height: 0, + top: 0, + left: 0, + right: 0, + }); + const { nonce } = React$1.useContext(MotionConfigContext); + /** + * We create and inject a style block so we can apply this explicit + * sizing in a non-destructive manner by just deleting the style block. + * + * We can't apply size via render as the measurement happens + * in getSnapshotBeforeUpdate (post-render), likewise if we apply the + * styles directly on the DOM node, we might be overwriting + * styles set via the style prop. + */ + React$1.useInsertionEffect(() => { + const { width, height, top, left, right } = size.current; + if (isPresent || !ref.current || !width || !height) + return; + const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`; + ref.current.dataset.motionPopId = id; + const style = document.createElement("style"); + if (nonce) + style.nonce = nonce; + const parent = root ?? document.head; + parent.appendChild(style); + if (style.sheet) { + style.sheet.insertRule(` + [data-motion-pop-id="${id}"] { + position: absolute !important; + width: ${width}px !important; + height: ${height}px !important; + ${x}px !important; + top: ${top}px !important; + } + `); + } + return () => { + parent.removeChild(style); + if (parent.contains(style)) { + parent.removeChild(style); + } + }; + }, [isPresent]); + return (jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) })); + } + + const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, root }) => { + const presenceChildren = useConstant(newChildrenMap); + const id = React$1.useId(); + let isReusedContext = true; + let context = React$1.useMemo(() => { + isReusedContext = false; + return { + id, + initial, + isPresent, + custom, + onExitComplete: (childId) => { + presenceChildren.set(childId, true); + for (const isComplete of presenceChildren.values()) { + if (!isComplete) + return; // can stop searching when any is incomplete + } + onExitComplete && onExitComplete(); + }, + register: (childId) => { + presenceChildren.set(childId, false); + return () => presenceChildren.delete(childId); + }, + }; + }, [isPresent, presenceChildren, onExitComplete]); + /** + * If the presence of a child affects the layout of the components around it, + * we want to make a new context value to ensure they get re-rendered + * so they can detect that layout change. + */ + if (presenceAffectsLayout && isReusedContext) { + context = { ...context }; + } + React$1.useMemo(() => { + presenceChildren.forEach((_, key) => presenceChildren.set(key, false)); + }, [isPresent]); + /** + * If there's no `motion` components to fire exit animations, we want to remove this + * component immediately. + */ + React__namespace.useEffect(() => { + !isPresent && + !presenceChildren.size && + onExitComplete && + onExitComplete(); + }, [isPresent]); + if (mode === "popLayout") { + children = (jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, root: root, children: children })); + } + return (jsx(PresenceContext.Provider, { value: context, children: children })); + }; + function newChildrenMap() { + return new Map(); + } + + /** + * When a component is the child of `AnimatePresence`, it can use `usePresence` + * to access information about whether it's still present in the React tree. + * + * ```jsx + * import { usePresence } from "framer-motion" + * + * export const Component = () => { + * const [isPresent, safeToRemove] = usePresence() + * + * useEffect(() => { + * !isPresent && setTimeout(safeToRemove, 1000) + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * If `isPresent` is `false`, it means that a component has been removed the tree, but + * `AnimatePresence` won't really remove it until `safeToRemove` has been called. + * + * @public + */ + function usePresence(subscribe = true) { + const context = React$1.useContext(PresenceContext); + if (context === null) + return [true, null]; + const { isPresent, onExitComplete, register } = context; + // It's safe to call the following hooks conditionally (after an early return) because the context will always + // either be null or non-null for the lifespan of the component. + const id = React$1.useId(); + React$1.useEffect(() => { + if (subscribe) { + return register(id); + } + }, [subscribe]); + const safeToRemove = React$1.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]); + return !isPresent && onExitComplete ? [false, safeToRemove] : [true]; + } + /** + * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present. + * There is no `safeToRemove` function. + * + * ```jsx + * import { useIsPresent } from "framer-motion" + * + * export const Component = () => { + * const isPresent = useIsPresent() + * + * useEffect(() => { + * !isPresent && console.log("I've been removed!") + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * @public + */ + function useIsPresent() { + return isPresent(React$1.useContext(PresenceContext)); + } + function isPresent(context) { + return context === null ? true : context.isPresent; + } + + const getChildKey = (child) => child.key || ""; + function onlyElements(children) { + const filtered = []; + // We use forEach here instead of map as map mutates the component key by preprending `.$` + React$1.Children.forEach(children, (child) => { + if (React$1.isValidElement(child)) + filtered.push(child); + }); + return filtered; + } + + /** + * `AnimatePresence` enables the animation of components that have been removed from the tree. + * + * When adding/removing more than a single child, every child **must** be given a unique `key` prop. + * + * Any `motion` components that have an `exit` property defined will animate out when removed from + * the tree. + * + * ```jsx + * import { motion, AnimatePresence } from 'framer-motion' + * + * export const Items = ({ items }) => ( + * + * {items.map(item => ( + * + * ))} + * + * ) + * ``` + * + * You can sequence exit animations throughout a tree using variants. + * + * If a child contains multiple `motion` components with `exit` props, it will only unmount the child + * once all `motion` components have finished animating out. Likewise, any components using + * `usePresence` all need to call `safeToRemove`. + * + * @public + */ + const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", root }) => { + const [isParentPresent, safeToRemove] = usePresence(propagate); + /** + * Filter any children that aren't ReactElements. We can only track components + * between renders with a props.key. + */ + const presentChildren = React$1.useMemo(() => onlyElements(children), [children]); + /** + * Track the keys of the currently rendered children. This is used to + * determine which children are exiting. + */ + const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey); + /** + * If `initial={false}` we only want to pass this to components in the first render. + */ + const isInitialRender = React$1.useRef(true); + /** + * A ref containing the currently present children. When all exit animations + * are complete, we use this to re-render the component with the latest children + * *committed* rather than the latest children *rendered*. + */ + const pendingPresentChildren = React$1.useRef(presentChildren); + /** + * Track which exiting children have finished animating out. + */ + const exitComplete = useConstant(() => new Map()); + /** + * Save children to render as React state. To ensure this component is concurrent-safe, + * we check for exiting children via an effect. + */ + const [diffedChildren, setDiffedChildren] = React$1.useState(presentChildren); + const [renderedChildren, setRenderedChildren] = React$1.useState(presentChildren); + useIsomorphicLayoutEffect(() => { + isInitialRender.current = false; + pendingPresentChildren.current = presentChildren; + /** + * Update complete status of exiting children. + */ + for (let i = 0; i < renderedChildren.length; i++) { + const key = getChildKey(renderedChildren[i]); + if (!presentKeys.includes(key)) { + if (exitComplete.get(key) !== true) { + exitComplete.set(key, false); + } + } + else { + exitComplete.delete(key); + } + } + }, [renderedChildren, presentKeys.length, presentKeys.join("-")]); + const exitingChildren = []; + if (presentChildren !== diffedChildren) { + let nextChildren = [...presentChildren]; + /** + * Loop through all the currently rendered components and decide which + * are exiting. + */ + for (let i = 0; i < renderedChildren.length; i++) { + const child = renderedChildren[i]; + const key = getChildKey(child); + if (!presentKeys.includes(key)) { + nextChildren.splice(i, 0, child); + exitingChildren.push(child); + } + } + /** + * If we're in "wait" mode, and we have exiting children, we want to + * only render these until they've all exited. + */ + if (mode === "wait" && exitingChildren.length) { + nextChildren = exitingChildren; + } + setRenderedChildren(onlyElements(nextChildren)); + setDiffedChildren(presentChildren); + /** + * Early return to ensure once we've set state with the latest diffed + * children, we can immediately re-render. + */ + return null; + } + if (mode === "wait" && + renderedChildren.length > 1) { + console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`); + } + /** + * If we've been provided a forceRender function by the LayoutGroupContext, + * we can use it to force a re-render amongst all surrounding components once + * all components have finished animating out. + */ + const { forceRender } = React$1.useContext(LayoutGroupContext); + return (jsx(Fragment, { children: renderedChildren.map((child) => { + const key = getChildKey(child); + const isPresent = propagate && !isParentPresent + ? false + : presentChildren === renderedChildren || + presentKeys.includes(key); + const onExit = () => { + if (exitComplete.has(key)) { + exitComplete.set(key, true); + } + else { + return; + } + let isEveryExitComplete = true; + exitComplete.forEach((isExitComplete) => { + if (!isExitComplete) + isEveryExitComplete = false; + }); + if (isEveryExitComplete) { + forceRender?.(); + setRenderedChildren(pendingPresentChildren.current); + propagate && safeToRemove?.(); + onExitComplete && onExitComplete(); + } + }; + return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial + ? undefined + : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, root: root, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key)); + }) })); + }; + + /** + * Note: Still used by components generated by old versions of Framer + * + * @deprecated + */ + const DeprecatedLayoutGroupContext = React$1.createContext(null); + + const SCALE_PRECISION = 0.0001; + const SCALE_MIN = 1 - SCALE_PRECISION; + const SCALE_MAX = 1 + SCALE_PRECISION; + const TRANSLATE_PRECISION = 0.01; + const TRANSLATE_MIN = 0 - TRANSLATE_PRECISION; + const TRANSLATE_MAX = 0 + TRANSLATE_PRECISION; + function calcLength(axis) { + return axis.max - axis.min; + } + function isNear(value, target, maxDistance) { + return Math.abs(value - target) <= maxDistance; + } + function calcAxisDelta(delta, source, target, origin = 0.5) { + delta.origin = origin; + delta.originPoint = mixNumber$1(source.min, source.max, delta.origin); + delta.scale = calcLength(target) / calcLength(source); + delta.translate = + mixNumber$1(target.min, target.max, delta.origin) - delta.originPoint; + if ((delta.scale >= SCALE_MIN && delta.scale <= SCALE_MAX) || + isNaN(delta.scale)) { + delta.scale = 1.0; + } + if ((delta.translate >= TRANSLATE_MIN && + delta.translate <= TRANSLATE_MAX) || + isNaN(delta.translate)) { + delta.translate = 0.0; + } + } + function calcBoxDelta(delta, source, target, origin) { + calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined); + calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined); + } + function calcRelativeAxis(target, relative, parent) { + target.min = parent.min + relative.min; + target.max = target.min + calcLength(relative); + } + function calcRelativeBox(target, relative, parent) { + calcRelativeAxis(target.x, relative.x, parent.x); + calcRelativeAxis(target.y, relative.y, parent.y); + } + function calcRelativeAxisPosition(target, layout, parent) { + target.min = layout.min - parent.min; + target.max = target.min + calcLength(layout); + } + function calcRelativePosition(target, layout, parent) { + calcRelativeAxisPosition(target.x, layout.x, parent.x); + calcRelativeAxisPosition(target.y, layout.y, parent.y); + } + + const notify = (node) => !node.isLayoutDirty && node.willUpdate(false); + function nodeGroup() { + const nodes = new Set(); + const subscriptions = new WeakMap(); + const dirtyAll = () => nodes.forEach(notify); + return { + add: (node) => { + nodes.add(node); + subscriptions.set(node, node.addEventListener("willUpdate", dirtyAll)); + }, + remove: (node) => { + nodes.delete(node); + const unsubscribe = subscriptions.get(node); + if (unsubscribe) { + unsubscribe(); + subscriptions.delete(node); + } + dirtyAll(); + }, + dirty: dirtyAll, + }; + } + + const isNotNull = (value) => value !== null; + function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const index = repeat && repeatType !== "loop" && repeat % 2 === 1 + ? 0 + : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; + } + + const underDampedSpring = { + type: "spring", + stiffness: 500, + damping: 25, + restSpeed: 10, + }; + const criticallyDampedSpring = (target) => ({ + type: "spring", + stiffness: 550, + damping: target === 0 ? 2 * Math.sqrt(550) : 30, + restSpeed: 10, + }); + const keyframesTransition = { + type: "keyframes", + duration: 0.8, + }; + /** + * Default easing curve is a slightly shallower version of + * the default browser easing curve. + */ + const ease = { + type: "keyframes", + ease: [0.25, 0.1, 0.35, 1], + duration: 0.3, + }; + const getDefaultTransition = (valueKey, { keyframes }) => { + if (keyframes.length > 2) { + return keyframesTransition; + } + else if (transformProps.has(valueKey)) { + return valueKey.startsWith("scale") + ? criticallyDampedSpring(keyframes[1]) + : underDampedSpring; + } + return ease; + }; + + /** + * Decide whether a transition is defined on a given Transition. + * This filters out orchestration options and returns true + * if any options are left. + */ + function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) { + return !!Object.keys(transition).length; + } + + const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => { + const valueTransition = getValueTransition$1(transition, name) || {}; + /** + * Most transition values are currently completely overwritten by value-specific + * transitions. In the future it'd be nicer to blend these transitions. But for now + * delay actually does inherit from the root transition if not value-specific. + */ + const delay = valueTransition.delay || transition.delay || 0; + /** + * Elapsed isn't a public transition option but can be passed through from + * optimized appear effects in milliseconds. + */ + let { elapsed = 0 } = transition; + elapsed = elapsed - secondsToMilliseconds(delay); + const options = { + keyframes: Array.isArray(target) ? target : [null, target], + ease: "easeOut", + velocity: value.getVelocity(), + ...valueTransition, + delay: -elapsed, + onUpdate: (v) => { + value.set(v); + valueTransition.onUpdate && valueTransition.onUpdate(v); + }, + onComplete: () => { + onComplete(); + valueTransition.onComplete && valueTransition.onComplete(); + }, + name, + motionValue: value, + element: isHandoff ? undefined : element, + }; + /** + * If there's no transition defined for this value, we can generate + * unique transition settings for this value. + */ + if (!isTransitionDefined(valueTransition)) { + Object.assign(options, getDefaultTransition(name, options)); + } + /** + * Both WAAPI and our internal animation functions use durations + * as defined by milliseconds, while our external API defines them + * as seconds. + */ + options.duration && (options.duration = secondsToMilliseconds(options.duration)); + options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay)); + /** + * Support deprecated way to set initial value. Prefer keyframe syntax. + */ + if (options.from !== undefined) { + options.keyframes[0] = options.from; + } + let shouldSkip = false; + if (options.type === false || + (options.duration === 0 && !options.repeatDelay)) { + options.duration = 0; + if (options.delay === 0) { + shouldSkip = true; + } + } + if (MotionGlobalConfig.instantAnimations || + MotionGlobalConfig.skipAnimations) { + shouldSkip = true; + options.duration = 0; + options.delay = 0; + } + /** + * If the transition type or easing has been explicitly set by the user + * then we don't want to allow flattening the animation. + */ + options.allowFlatten = !valueTransition.type && !valueTransition.ease; + /** + * If we can or must skip creating the animation, and apply only + * the final keyframe, do so. We also check once keyframes are resolved but + * this early check prevents the need to create an animation at all. + */ + if (shouldSkip && !isHandoff && value.get() !== undefined) { + const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition); + if (finalKeyframe !== undefined) { + frame.update(() => { + options.onUpdate(finalKeyframe); + options.onComplete(); + }); + return; + } + } + return valueTransition.isSync + ? new JSAnimation(options) + : new AsyncMotionValueAnimation(options); + }; + + function animateSingleValue(value, keyframes, options) { + const motionValue$1 = isMotionValue(value) ? value : motionValue(value); + motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options)); + return motionValue$1.animation; + } + + /** + * Convert camelCase to dash-case properties. + */ + const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase(); + + const optimizedAppearDataId = "framerAppearId"; + const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId); + + function getOptimisedAppearId(visualElement) { + return visualElement.props[optimizedAppearDataAttribute]; + } + + const compareByDepth = (a, b) => a.depth - b.depth; + + class FlatTree { + constructor() { + this.children = []; + this.isDirty = false; + } + add(child) { + addUniqueItem(this.children, child); + this.isDirty = true; + } + remove(child) { + removeItem(this.children, child); + this.isDirty = true; + } + forEach(callback) { + this.isDirty && this.children.sort(compareByDepth); + this.isDirty = false; + this.children.forEach(callback); + } + } + + /** + * Timeout defined in ms + */ + function delay(callback, timeout) { + const start = time.now(); + const checkElapsed = ({ timestamp }) => { + const elapsed = timestamp - start; + if (elapsed >= timeout) { + cancelFrame(checkElapsed); + callback(elapsed - timeout); + } + }; + frame.setup(checkElapsed, true); + return () => cancelFrame(checkElapsed); + } + + /** + * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself + * + * TODO: Remove and move to library + */ + function resolveMotionValue(value) { + return isMotionValue(value) ? value.get() : value; + } + + const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"]; + const numBorders = borders.length; + const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value; + const isPx = (value) => typeof value === "number" || px.test(value); + function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) { + if (shouldCrossfadeOpacity) { + target.opacity = mixNumber$1(0, lead.opacity ?? 1, easeCrossfadeIn(progress)); + target.opacityExit = mixNumber$1(follow.opacity ?? 1, 0, easeCrossfadeOut(progress)); + } + else if (isOnlyMember) { + target.opacity = mixNumber$1(follow.opacity ?? 1, lead.opacity ?? 1, progress); + } + /** + * Mix border radius + */ + for (let i = 0; i < numBorders; i++) { + const borderLabel = `border${borders[i]}Radius`; + let followRadius = getRadius(follow, borderLabel); + let leadRadius = getRadius(lead, borderLabel); + if (followRadius === undefined && leadRadius === undefined) + continue; + followRadius || (followRadius = 0); + leadRadius || (leadRadius = 0); + const canMix = followRadius === 0 || + leadRadius === 0 || + isPx(followRadius) === isPx(leadRadius); + if (canMix) { + target[borderLabel] = Math.max(mixNumber$1(asNumber(followRadius), asNumber(leadRadius), progress), 0); + if (percent.test(leadRadius) || percent.test(followRadius)) { + target[borderLabel] += "%"; + } + } + else { + target[borderLabel] = leadRadius; + } + } + /** + * Mix rotation + */ + if (follow.rotate || lead.rotate) { + target.rotate = mixNumber$1(follow.rotate || 0, lead.rotate || 0, progress); + } + } + function getRadius(values, radiusName) { + return values[radiusName] !== undefined + ? values[radiusName] + : values.borderRadius; + } + // /** + // * We only want to mix the background color if there's a follow element + // * that we're not crossfading opacity between. For instance with switch + // * AnimateSharedLayout animations, this helps the illusion of a continuous + // * element being animated but also cuts down on the number of paints triggered + // * for elements where opacity is doing that work for us. + // */ + // if ( + // !hasFollowElement && + // latestLeadValues.backgroundColor && + // latestFollowValues.backgroundColor + // ) { + // /** + // * This isn't ideal performance-wise as mixColor is creating a new function every frame. + // * We could probably create a mixer that runs at the start of the animation but + // * the idea behind the crossfader is that it runs dynamically between two potentially + // * changing targets (ie opacity or borderRadius may be animating independently via variants) + // */ + // leadState.backgroundColor = followState.backgroundColor = mixColor( + // latestFollowValues.backgroundColor as string, + // latestLeadValues.backgroundColor as string + // )(p) + // } + const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut); + const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop); + function compress(min, max, easing) { + return (p) => { + // Could replace ifs with clamp + if (p < min) + return 0; + if (p > max) + return 1; + return easing(progress(min, max, p)); + }; + } + + /** + * Reset an axis to the provided origin box. + * + * This is a mutative operation. + */ + function copyAxisInto(axis, originAxis) { + axis.min = originAxis.min; + axis.max = originAxis.max; + } + /** + * Reset a box to the provided origin box. + * + * This is a mutative operation. + */ + function copyBoxInto(box, originBox) { + copyAxisInto(box.x, originBox.x); + copyAxisInto(box.y, originBox.y); + } + /** + * Reset a delta to the provided origin box. + * + * This is a mutative operation. + */ + function copyAxisDeltaInto(delta, originDelta) { + delta.translate = originDelta.translate; + delta.scale = originDelta.scale; + delta.originPoint = originDelta.originPoint; + delta.origin = originDelta.origin; + } + + function isIdentityScale(scale) { + return scale === undefined || scale === 1; + } + function hasScale({ scale, scaleX, scaleY }) { + return (!isIdentityScale(scale) || + !isIdentityScale(scaleX) || + !isIdentityScale(scaleY)); + } + function hasTransform(values) { + return (hasScale(values) || + has2DTranslate(values) || + values.z || + values.rotate || + values.rotateX || + values.rotateY || + values.skewX || + values.skewY); + } + function has2DTranslate(values) { + return is2DTranslate(values.x) || is2DTranslate(values.y); + } + function is2DTranslate(value) { + return value && value !== "0%"; + } + + /** + * Scales a point based on a factor and an originPoint + */ + function scalePoint(point, scale, originPoint) { + const distanceFromOrigin = point - originPoint; + const scaled = scale * distanceFromOrigin; + return originPoint + scaled; + } + /** + * Applies a translate/scale delta to a point + */ + function applyPointDelta(point, translate, scale, originPoint, boxScale) { + if (boxScale !== undefined) { + point = scalePoint(point, boxScale, originPoint); + } + return scalePoint(point, scale, originPoint) + translate; + } + /** + * Applies a translate/scale delta to an axis + */ + function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) { + axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale); + axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale); + } + /** + * Applies a translate/scale delta to a box + */ + function applyBoxDelta(box, { x, y }) { + applyAxisDelta(box.x, x.translate, x.scale, x.originPoint); + applyAxisDelta(box.y, y.translate, y.scale, y.originPoint); + } + const TREE_SCALE_SNAP_MIN = 0.999999999999; + const TREE_SCALE_SNAP_MAX = 1.0000000000001; + /** + * Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms + * in a tree upon our box before then calculating how to project it into our desired viewport-relative box + * + * This is the final nested loop within updateLayoutDelta for future refactoring + */ + function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) { + const treeLength = treePath.length; + if (!treeLength) + return; + // Reset the treeScale + treeScale.x = treeScale.y = 1; + let node; + let delta; + for (let i = 0; i < treeLength; i++) { + node = treePath[i]; + delta = node.projectionDelta; + /** + * TODO: Prefer to remove this, but currently we have motion components with + * display: contents in Framer. + */ + const { visualElement } = node.options; + if (visualElement && + visualElement.props.style && + visualElement.props.style.display === "contents") { + continue; + } + if (isSharedTransition && + node.options.layoutScroll && + node.scroll && + node !== node.root) { + transformBox(box, { + x: -node.scroll.offset.x, + y: -node.scroll.offset.y, + }); + } + if (delta) { + // Incoporate each ancestor's scale into a culmulative treeScale for this component + treeScale.x *= delta.x.scale; + treeScale.y *= delta.y.scale; + // Apply each ancestor's calculated delta into this component's recorded layout box + applyBoxDelta(box, delta); + } + if (isSharedTransition && hasTransform(node.latestValues)) { + transformBox(box, node.latestValues); + } + } + /** + * Snap tree scale back to 1 if it's within a non-perceivable threshold. + * This will help reduce useless scales getting rendered. + */ + if (treeScale.x < TREE_SCALE_SNAP_MAX && + treeScale.x > TREE_SCALE_SNAP_MIN) { + treeScale.x = 1.0; + } + if (treeScale.y < TREE_SCALE_SNAP_MAX && + treeScale.y > TREE_SCALE_SNAP_MIN) { + treeScale.y = 1.0; + } + } + function translateAxis(axis, distance) { + axis.min = axis.min + distance; + axis.max = axis.max + distance; + } + /** + * Apply a transform to an axis from the latest resolved motion values. + * This function basically acts as a bridge between a flat motion value map + * and applyAxisDelta + */ + function transformAxis(axis, axisTranslate, axisScale, boxScale, axisOrigin = 0.5) { + const originPoint = mixNumber$1(axis.min, axis.max, axisOrigin); + // Apply the axis delta to the final axis + applyAxisDelta(axis, axisTranslate, axisScale, originPoint, boxScale); + } + /** + * Apply a transform to a box from the latest resolved motion values. + */ + function transformBox(box, transform) { + transformAxis(box.x, transform.x, transform.scaleX, transform.scale, transform.originX); + transformAxis(box.y, transform.y, transform.scaleY, transform.scale, transform.originY); + } + + /** + * Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse + */ + function removePointDelta(point, translate, scale, originPoint, boxScale) { + point -= translate; + point = scalePoint(point, 1 / scale, originPoint); + if (boxScale !== undefined) { + point = scalePoint(point, 1 / boxScale, originPoint); + } + return point; + } + /** + * Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse + */ + function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) { + if (percent.test(translate)) { + translate = parseFloat(translate); + const relativeProgress = mixNumber$1(sourceAxis.min, sourceAxis.max, translate / 100); + translate = relativeProgress - sourceAxis.min; + } + if (typeof translate !== "number") + return; + let originPoint = mixNumber$1(originAxis.min, originAxis.max, origin); + if (axis === originAxis) + originPoint -= translate; + axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale); + axis.max = removePointDelta(axis.max, translate, scale, originPoint, boxScale); + } + /** + * Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse + * and acts as a bridge between motion values and removeAxisDelta + */ + function removeAxisTransforms(axis, transforms, [key, scaleKey, originKey], origin, sourceAxis) { + removeAxisDelta(axis, transforms[key], transforms[scaleKey], transforms[originKey], transforms.scale, origin, sourceAxis); + } + /** + * The names of the motion values we want to apply as translation, scale and origin. + */ + const xKeys = ["x", "scaleX", "originX"]; + const yKeys = ["y", "scaleY", "originY"]; + /** + * Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse + * and acts as a bridge between motion values and removeAxisDelta + */ + function removeBoxTransforms(box, transforms, originBox, sourceBox) { + removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined); + removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined); + } + + const createAxisDelta = () => ({ + translate: 0, + scale: 1, + origin: 0, + originPoint: 0, + }); + const createDelta = () => ({ + x: createAxisDelta(), + y: createAxisDelta(), + }); + const createAxis = () => ({ min: 0, max: 0 }); + const createBox = () => ({ + x: createAxis(), + y: createAxis(), + }); + + function isAxisDeltaZero(delta) { + return delta.translate === 0 && delta.scale === 1; + } + function isDeltaZero(delta) { + return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y); + } + function axisEquals(a, b) { + return a.min === b.min && a.max === b.max; + } + function boxEquals(a, b) { + return axisEquals(a.x, b.x) && axisEquals(a.y, b.y); + } + function axisEqualsRounded(a, b) { + return (Math.round(a.min) === Math.round(b.min) && + Math.round(a.max) === Math.round(b.max)); + } + function boxEqualsRounded(a, b) { + return axisEqualsRounded(a.x, b.x) && axisEqualsRounded(a.y, b.y); + } + function aspectRatio(box) { + return calcLength(box.x) / calcLength(box.y); + } + function axisDeltaEquals(a, b) { + return (a.translate === b.translate && + a.scale === b.scale && + a.originPoint === b.originPoint); + } + + class NodeStack { + constructor() { + this.members = []; + } + add(node) { + addUniqueItem(this.members, node); + node.scheduleRender(); + } + remove(node) { + removeItem(this.members, node); + if (node === this.prevLead) { + this.prevLead = undefined; + } + if (node === this.lead) { + const prevLead = this.members[this.members.length - 1]; + if (prevLead) { + this.promote(prevLead); + } + } + } + relegate(node) { + const indexOfNode = this.members.findIndex((member) => node === member); + if (indexOfNode === 0) + return false; + /** + * Find the next projection node that is present + */ + let prevLead; + for (let i = indexOfNode; i >= 0; i--) { + const member = this.members[i]; + if (member.isPresent !== false) { + prevLead = member; + break; + } + } + if (prevLead) { + this.promote(prevLead); + return true; + } + else { + return false; + } + } + promote(node, preserveFollowOpacity) { + const prevLead = this.lead; + if (node === prevLead) + return; + this.prevLead = prevLead; + this.lead = node; + node.show(); + if (prevLead) { + prevLead.instance && prevLead.scheduleRender(); + node.scheduleRender(); + node.resumeFrom = prevLead; + if (preserveFollowOpacity) { + node.resumeFrom.preserveOpacity = true; + } + if (prevLead.snapshot) { + node.snapshot = prevLead.snapshot; + node.snapshot.latestValues = + prevLead.animationValues || prevLead.latestValues; + } + if (node.root && node.root.isUpdating) { + node.isLayoutDirty = true; + } + const { crossfade } = node.options; + if (crossfade === false) { + prevLead.hide(); + } + /** + * TODO: + * - Test border radius when previous node was deleted + * - boxShadow mixing + * - Shared between element A in scrolled container and element B (scroll stays the same or changes) + * - Shared between element A in transformed container and element B (transform stays the same or changes) + * - Shared between element A in scrolled page and element B (scroll stays the same or changes) + * --- + * - Crossfade opacity of root nodes + * - layoutId changes after animation + * - layoutId changes mid animation + */ + } + } + exitAnimationComplete() { + this.members.forEach((node) => { + const { options, resumingFrom } = node; + options.onExitComplete && options.onExitComplete(); + if (resumingFrom) { + resumingFrom.options.onExitComplete && + resumingFrom.options.onExitComplete(); + } + }); + } + scheduleRender() { + this.members.forEach((node) => { + node.instance && node.scheduleRender(false); + }); + } + /** + * Clear any leads that have been removed this render to prevent them from being + * used in future animations and to prevent memory leaks + */ + removeLeadSnapshot() { + if (this.lead && this.lead.snapshot) { + this.lead.snapshot = undefined; + } + } + } + + const scaleCorrectors = {}; + function addScaleCorrector(correctors) { + for (const key in correctors) { + scaleCorrectors[key] = correctors[key]; + if (isCSSVariableName(key)) { + scaleCorrectors[key].isCSSVariable = true; + } + } + } + + function buildProjectionTransform(delta, treeScale, latestTransform) { + let transform = ""; + /** + * The translations we use to calculate are always relative to the viewport coordinate space. + * But when we apply scales, we also scale the coordinate space of an element and its children. + * For instance if we have a treeScale (the culmination of all parent scales) of 0.5 and we need + * to move an element 100 pixels, we actually need to move it 200 in within that scaled space. + */ + const xTranslate = delta.x.translate / treeScale.x; + const yTranslate = delta.y.translate / treeScale.y; + const zTranslate = latestTransform?.z || 0; + if (xTranslate || yTranslate || zTranslate) { + transform = `translate3d(${xTranslate}px, ${yTranslate}px, ${zTranslate}px) `; + } + /** + * Apply scale correction for the tree transform. + * This will apply scale to the screen-orientated axes. + */ + if (treeScale.x !== 1 || treeScale.y !== 1) { + transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `; + } + if (latestTransform) { + const { transformPerspective, rotate, rotateX, rotateY, skewX, skewY } = latestTransform; + if (transformPerspective) + transform = `perspective(${transformPerspective}px) ${transform}`; + if (rotate) + transform += `rotate(${rotate}deg) `; + if (rotateX) + transform += `rotateX(${rotateX}deg) `; + if (rotateY) + transform += `rotateY(${rotateY}deg) `; + if (skewX) + transform += `skewX(${skewX}deg) `; + if (skewY) + transform += `skewY(${skewY}deg) `; + } + /** + * Apply scale to match the size of the element to the size we want it. + * This will apply scale to the element-orientated axes. + */ + const elementScaleX = delta.x.scale * treeScale.x; + const elementScaleY = delta.y.scale * treeScale.y; + if (elementScaleX !== 1 || elementScaleY !== 1) { + transform += `scale(${elementScaleX}, ${elementScaleY})`; + } + return transform || "none"; + } + + function eachAxis(callback) { + return [callback("x"), callback("y")]; + } + + /** + * This should only ever be modified on the client otherwise it'll + * persist through server requests. If we need instanced states we + * could lazy-init via root. + */ + const globalProjectionState = { + /** + * Global flag as to whether the tree has animated since the last time + * we resized the window + */ + hasAnimatedSinceResize: true, + /** + * We set this to true once, on the first update. Any nodes added to the tree beyond that + * update will be given a `data-projection-id` attribute. + */ + hasEverUpdated: false, + }; + + const metrics = { + nodes: 0, + calculatedTargetDeltas: 0, + calculatedProjections: 0, + }; + const transformAxes = ["", "X", "Y", "Z"]; + /** + * We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1 + * which has a noticeable difference in spring animations + */ + const animationTarget = 1000; + let id$2 = 0; + function resetDistortingTransform(key, visualElement, values, sharedAnimationValues) { + const { latestValues } = visualElement; + // Record the distorting transform and then temporarily set it to 0 + if (latestValues[key]) { + values[key] = latestValues[key]; + visualElement.setStaticValue(key, 0); + if (sharedAnimationValues) { + sharedAnimationValues[key] = 0; + } + } + } + function cancelTreeOptimisedTransformAnimations(projectionNode) { + projectionNode.hasCheckedOptimisedAppear = true; + if (projectionNode.root === projectionNode) + return; + const { visualElement } = projectionNode.options; + if (!visualElement) + return; + const appearId = getOptimisedAppearId(visualElement); + if (window.MotionHasOptimisedAnimation(appearId, "transform")) { + const { layout, layoutId } = projectionNode.options; + window.MotionCancelOptimisedAnimation(appearId, "transform", frame, !(layout || layoutId)); + } + const { parent } = projectionNode; + if (parent && !parent.hasCheckedOptimisedAppear) { + cancelTreeOptimisedTransformAnimations(parent); + } + } + function createProjectionNode$1({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) { + return class ProjectionNode { + constructor(latestValues = {}, parent = defaultParent?.()) { + /** + * A unique ID generated for every projection node. + */ + this.id = id$2++; + /** + * An id that represents a unique session instigated by startUpdate. + */ + this.animationId = 0; + this.animationCommitId = 0; + /** + * A Set containing all this component's children. This is used to iterate + * through the children. + * + * TODO: This could be faster to iterate as a flat array stored on the root node. + */ + this.children = new Set(); + /** + * Options for the node. We use this to configure what kind of layout animations + * we should perform (if any). + */ + this.options = {}; + /** + * We use this to detect when its safe to shut down part of a projection tree. + * We have to keep projecting children for scale correction and relative projection + * until all their parents stop performing layout animations. + */ + this.isTreeAnimating = false; + this.isAnimationBlocked = false; + /** + * Flag to true if we think this layout has been changed. We can't always know this, + * currently we set it to true every time a component renders, or if it has a layoutDependency + * if that has changed between renders. Additionally, components can be grouped by LayoutGroup + * and if one node is dirtied, they all are. + */ + this.isLayoutDirty = false; + /** + * Flag to true if we think the projection calculations for this node needs + * recalculating as a result of an updated transform or layout animation. + */ + this.isProjectionDirty = false; + /** + * Flag to true if the layout *or* transform has changed. This then gets propagated + * throughout the projection tree, forcing any element below to recalculate on the next frame. + */ + this.isSharedProjectionDirty = false; + /** + * Flag transform dirty. This gets propagated throughout the whole tree but is only + * respected by shared nodes. + */ + this.isTransformDirty = false; + /** + * Block layout updates for instant layout transitions throughout the tree. + */ + this.updateManuallyBlocked = false; + this.updateBlockedByResize = false; + /** + * Set to true between the start of the first `willUpdate` call and the end of the `didUpdate` + * call. + */ + this.isUpdating = false; + /** + * If this is an SVG element we currently disable projection transforms + */ + this.isSVG = false; + /** + * Flag to true (during promotion) if a node doing an instant layout transition needs to reset + * its projection styles. + */ + this.needsReset = false; + /** + * Flags whether this node should have its transform reset prior to measuring. + */ + this.shouldResetTransform = false; + /** + * Store whether this node has been checked for optimised appear animations. As + * effects fire bottom-up, and we want to look up the tree for appear animations, + * this makes sure we only check each path once, stopping at nodes that + * have already been checked. + */ + this.hasCheckedOptimisedAppear = false; + /** + * An object representing the calculated contextual/accumulated/tree scale. + * This will be used to scale calculcated projection transforms, as these are + * calculated in screen-space but need to be scaled for elements to layoutly + * make it to their calculated destinations. + * + * TODO: Lazy-init + */ + this.treeScale = { x: 1, y: 1 }; + /** + * + */ + this.eventHandlers = new Map(); + this.hasTreeAnimated = false; + // Note: Currently only running on root node + this.updateScheduled = false; + this.scheduleUpdate = () => this.update(); + this.projectionUpdateScheduled = false; + this.checkUpdateFailed = () => { + if (this.isUpdating) { + this.isUpdating = false; + this.clearAllSnapshots(); + } + }; + /** + * This is a multi-step process as shared nodes might be of different depths. Nodes + * are sorted by depth order, so we need to resolve the entire tree before moving to + * the next step. + */ + this.updateProjection = () => { + this.projectionUpdateScheduled = false; + /** + * Reset debug counts. Manually resetting rather than creating a new + * object each frame. + */ + if (statsBuffer.value) { + metrics.nodes = + metrics.calculatedTargetDeltas = + metrics.calculatedProjections = + 0; + } + this.nodes.forEach(propagateDirtyNodes); + this.nodes.forEach(resolveTargetDelta); + this.nodes.forEach(calcProjection); + this.nodes.forEach(cleanDirtyNodes); + if (statsBuffer.addProjectionMetrics) { + statsBuffer.addProjectionMetrics(metrics); + } + }; + /** + * Frame calculations + */ + this.resolvedRelativeTargetAt = 0.0; + this.hasProjected = false; + this.isVisible = true; + this.animationProgress = 0; + /** + * Shared layout + */ + // TODO Only running on root node + this.sharedNodes = new Map(); + this.latestValues = latestValues; + this.root = parent ? parent.root || parent : this; + this.path = parent ? [...parent.path, parent] : []; + this.parent = parent; + this.depth = parent ? parent.depth + 1 : 0; + for (let i = 0; i < this.path.length; i++) { + this.path[i].shouldResetTransform = true; + } + if (this.root === this) + this.nodes = new FlatTree(); + } + addEventListener(name, handler) { + if (!this.eventHandlers.has(name)) { + this.eventHandlers.set(name, new SubscriptionManager()); + } + return this.eventHandlers.get(name).add(handler); + } + notifyListeners(name, ...args) { + const subscriptionManager = this.eventHandlers.get(name); + subscriptionManager && subscriptionManager.notify(...args); + } + hasListeners(name) { + return this.eventHandlers.has(name); + } + /** + * Lifecycles + */ + mount(instance) { + if (this.instance) + return; + this.isSVG = isSVGElement(instance) && !isSVGSVGElement(instance); + this.instance = instance; + const { layoutId, layout, visualElement } = this.options; + if (visualElement && !visualElement.current) { + visualElement.mount(instance); + } + this.root.nodes.add(this); + this.parent && this.parent.children.add(this); + if (this.root.hasTreeAnimated && (layout || layoutId)) { + this.isLayoutDirty = true; + } + if (attachResizeListener) { + let cancelDelay; + let innerWidth = 0; + const resizeUnblockUpdate = () => (this.root.updateBlockedByResize = false); + // Set initial innerWidth in a frame.read callback to batch the read + frame.read(() => { + innerWidth = window.innerWidth; + }); + attachResizeListener(instance, () => { + const newInnerWidth = window.innerWidth; + if (newInnerWidth === innerWidth) + return; + innerWidth = newInnerWidth; + this.root.updateBlockedByResize = true; + cancelDelay && cancelDelay(); + cancelDelay = delay(resizeUnblockUpdate, 250); + if (globalProjectionState.hasAnimatedSinceResize) { + globalProjectionState.hasAnimatedSinceResize = false; + this.nodes.forEach(finishAnimation); + } + }); + } + if (layoutId) { + this.root.registerSharedNode(layoutId, this); + } + // Only register the handler if it requires layout animation + if (this.options.animate !== false && + visualElement && + (layoutId || layout)) { + this.addEventListener("didUpdate", ({ delta, hasLayoutChanged, hasRelativeLayoutChanged, layout: newLayout, }) => { + if (this.isTreeAnimationBlocked()) { + this.target = undefined; + this.relativeTarget = undefined; + return; + } + // TODO: Check here if an animation exists + const layoutTransition = this.options.transition || + visualElement.getDefaultTransition() || + defaultLayoutTransition; + const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps(); + /** + * The target layout of the element might stay the same, + * but its position relative to its parent has changed. + */ + const hasTargetChanged = !this.targetLayout || + !boxEqualsRounded(this.targetLayout, newLayout); + /* + * Note: Disabled to fix relative animations always triggering new + * layout animations. If this causes further issues, we can try + * a different approach to detecting relative target changes. + */ + // || hasRelativeLayoutChanged + /** + * If the layout hasn't seemed to have changed, it might be that the + * element is visually in the same place in the document but its position + * relative to its parent has indeed changed. So here we check for that. + */ + const hasOnlyRelativeTargetChanged = !hasLayoutChanged && hasRelativeLayoutChanged; + if (this.options.layoutRoot || + this.resumeFrom || + hasOnlyRelativeTargetChanged || + (hasLayoutChanged && + (hasTargetChanged || !this.currentAnimation))) { + if (this.resumeFrom) { + this.resumingFrom = this.resumeFrom; + this.resumingFrom.resumingFrom = undefined; + } + const animationOptions = { + ...getValueTransition$1(layoutTransition, "layout"), + onPlay: onLayoutAnimationStart, + onComplete: onLayoutAnimationComplete, + }; + if (visualElement.shouldReduceMotion || + this.options.layoutRoot) { + animationOptions.delay = 0; + animationOptions.type = false; + } + this.startAnimation(animationOptions); + /** + * Set animation origin after starting animation to avoid layout jump + * caused by stopping previous layout animation + */ + this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged); + } + else { + /** + * If the layout hasn't changed and we have an animation that hasn't started yet, + * finish it immediately. Otherwise it will be animating from a location + * that was probably never commited to screen and look like a jumpy box. + */ + if (!hasLayoutChanged) { + finishAnimation(this); + } + if (this.isLead() && this.options.onExitComplete) { + this.options.onExitComplete(); + } + } + this.targetLayout = newLayout; + }); + } + } + unmount() { + this.options.layoutId && this.willUpdate(); + this.root.nodes.remove(this); + const stack = this.getStack(); + stack && stack.remove(this); + this.parent && this.parent.children.delete(this); + this.instance = undefined; + this.eventHandlers.clear(); + cancelFrame(this.updateProjection); + } + // only on the root + blockUpdate() { + this.updateManuallyBlocked = true; + } + unblockUpdate() { + this.updateManuallyBlocked = false; + } + isUpdateBlocked() { + return this.updateManuallyBlocked || this.updateBlockedByResize; + } + isTreeAnimationBlocked() { + return (this.isAnimationBlocked || + (this.parent && this.parent.isTreeAnimationBlocked()) || + false); + } + // Note: currently only running on root node + startUpdate() { + if (this.isUpdateBlocked()) + return; + this.isUpdating = true; + this.nodes && this.nodes.forEach(resetSkewAndRotation); + this.animationId++; + } + getTransformTemplate() { + const { visualElement } = this.options; + return visualElement && visualElement.getProps().transformTemplate; + } + willUpdate(shouldNotifyListeners = true) { + this.root.hasTreeAnimated = true; + if (this.root.isUpdateBlocked()) { + this.options.onExitComplete && this.options.onExitComplete(); + return; + } + /** + * If we're running optimised appear animations then these must be + * cancelled before measuring the DOM. This is so we can measure + * the true layout of the element rather than the WAAPI animation + * which will be unaffected by the resetSkewAndRotate step. + * + * Note: This is a DOM write. Worst case scenario is this is sandwiched + * between other snapshot reads which will cause unnecessary style recalculations. + * This has to happen here though, as we don't yet know which nodes will need + * snapshots in startUpdate(), but we only want to cancel optimised animations + * if a layout animation measurement is actually going to be affected by them. + */ + if (window.MotionCancelOptimisedAnimation && + !this.hasCheckedOptimisedAppear) { + cancelTreeOptimisedTransformAnimations(this); + } + !this.root.isUpdating && this.root.startUpdate(); + if (this.isLayoutDirty) + return; + this.isLayoutDirty = true; + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + node.shouldResetTransform = true; + node.updateScroll("snapshot"); + if (node.options.layoutRoot) { + node.willUpdate(false); + } + } + const { layoutId, layout } = this.options; + if (layoutId === undefined && !layout) + return; + const transformTemplate = this.getTransformTemplate(); + this.prevTransformTemplateValue = transformTemplate + ? transformTemplate(this.latestValues, "") + : undefined; + this.updateSnapshot(); + shouldNotifyListeners && this.notifyListeners("willUpdate"); + } + update() { + this.updateScheduled = false; + const updateWasBlocked = this.isUpdateBlocked(); + // When doing an instant transition, we skip the layout update, + // but should still clean up the measurements so that the next + // snapshot could be taken correctly. + if (updateWasBlocked) { + this.unblockUpdate(); + this.clearAllSnapshots(); + this.nodes.forEach(clearMeasurements); + return; + } + /** + * If this is a repeat of didUpdate then ignore the animation. + */ + if (this.animationId <= this.animationCommitId) { + this.nodes.forEach(clearIsLayoutDirty); + return; + } + this.animationCommitId = this.animationId; + if (!this.isUpdating) { + this.nodes.forEach(clearIsLayoutDirty); + } + else { + this.isUpdating = false; + /** + * Write + */ + this.nodes.forEach(resetTransformStyle); + /** + * Read ================== + */ + // Update layout measurements of updated children + this.nodes.forEach(updateLayout); + /** + * Write + */ + // Notify listeners that the layout is updated + this.nodes.forEach(notifyLayoutUpdate); + } + this.clearAllSnapshots(); + /** + * Manually flush any pending updates. Ideally + * we could leave this to the following requestAnimationFrame but this seems + * to leave a flash of incorrectly styled content. + */ + const now = time.now(); + frameData.delta = clamp(0, 1000 / 60, now - frameData.timestamp); + frameData.timestamp = now; + frameData.isProcessing = true; + frameSteps.update.process(frameData); + frameSteps.preRender.process(frameData); + frameSteps.render.process(frameData); + frameData.isProcessing = false; + } + didUpdate() { + if (!this.updateScheduled) { + this.updateScheduled = true; + microtask.read(this.scheduleUpdate); + } + } + clearAllSnapshots() { + this.nodes.forEach(clearSnapshot); + this.sharedNodes.forEach(removeLeadSnapshots); + } + scheduleUpdateProjection() { + if (!this.projectionUpdateScheduled) { + this.projectionUpdateScheduled = true; + frame.preRender(this.updateProjection, false, true); + } + } + scheduleCheckAfterUnmount() { + /** + * If the unmounting node is in a layoutGroup and did trigger a willUpdate, + * we manually call didUpdate to give a chance to the siblings to animate. + * Otherwise, cleanup all snapshots to prevents future nodes from reusing them. + */ + frame.postRender(() => { + if (this.isLayoutDirty) { + this.root.didUpdate(); + } + else { + this.root.checkUpdateFailed(); + } + }); + } + /** + * Update measurements + */ + updateSnapshot() { + if (this.snapshot || !this.instance) + return; + this.snapshot = this.measure(); + if (this.snapshot && + !calcLength(this.snapshot.measuredBox.x) && + !calcLength(this.snapshot.measuredBox.y)) { + this.snapshot = undefined; + } + } + updateLayout() { + if (!this.instance) + return; + this.updateScroll(); + if (!(this.options.alwaysMeasureLayout && this.isLead()) && + !this.isLayoutDirty) { + return; + } + /** + * When a node is mounted, it simply resumes from the prevLead's + * snapshot instead of taking a new one, but the ancestors scroll + * might have updated while the prevLead is unmounted. We need to + * update the scroll again to make sure the layout we measure is + * up to date. + */ + if (this.resumeFrom && !this.resumeFrom.instance) { + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + node.updateScroll(); + } + } + const prevLayout = this.layout; + this.layout = this.measure(false); + this.layoutCorrected = createBox(); + this.isLayoutDirty = false; + this.projectionDelta = undefined; + this.notifyListeners("measure", this.layout.layoutBox); + const { visualElement } = this.options; + visualElement && + visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined); + } + updateScroll(phase = "measure") { + let needsMeasurement = Boolean(this.options.layoutScroll && this.instance); + if (this.scroll && + this.scroll.animationId === this.root.animationId && + this.scroll.phase === phase) { + needsMeasurement = false; + } + if (needsMeasurement && this.instance) { + const isRoot = checkIsScrollRoot(this.instance); + this.scroll = { + animationId: this.root.animationId, + phase, + isRoot, + offset: measureScroll(this.instance), + wasRoot: this.scroll ? this.scroll.isRoot : isRoot, + }; + } + } + resetTransform() { + if (!resetTransform) + return; + const isResetRequested = this.isLayoutDirty || + this.shouldResetTransform || + this.options.alwaysMeasureLayout; + const hasProjection = this.projectionDelta && !isDeltaZero(this.projectionDelta); + const transformTemplate = this.getTransformTemplate(); + const transformTemplateValue = transformTemplate + ? transformTemplate(this.latestValues, "") + : undefined; + const transformTemplateHasChanged = transformTemplateValue !== this.prevTransformTemplateValue; + if (isResetRequested && + this.instance && + (hasProjection || + hasTransform(this.latestValues) || + transformTemplateHasChanged)) { + resetTransform(this.instance, transformTemplateValue); + this.shouldResetTransform = false; + this.scheduleRender(); + } + } + measure(removeTransform = true) { + const pageBox = this.measurePageBox(); + let layoutBox = this.removeElementScroll(pageBox); + /** + * Measurements taken during the pre-render stage + * still have transforms applied so we remove them + * via calculation. + */ + if (removeTransform) { + layoutBox = this.removeTransform(layoutBox); + } + roundBox(layoutBox); + return { + animationId: this.root.animationId, + measuredBox: pageBox, + layoutBox, + latestValues: {}, + source: this.id, + }; + } + measurePageBox() { + const { visualElement } = this.options; + if (!visualElement) + return createBox(); + const box = visualElement.measureViewportBox(); + const wasInScrollRoot = this.scroll?.wasRoot || this.path.some(checkNodeWasScrollRoot); + if (!wasInScrollRoot) { + // Remove viewport scroll to give page-relative coordinates + const { scroll } = this.root; + if (scroll) { + translateAxis(box.x, scroll.offset.x); + translateAxis(box.y, scroll.offset.y); + } + } + return box; + } + removeElementScroll(box) { + const boxWithoutScroll = createBox(); + copyBoxInto(boxWithoutScroll, box); + if (this.scroll?.wasRoot) { + return boxWithoutScroll; + } + /** + * Performance TODO: Keep a cumulative scroll offset down the tree + * rather than loop back up the path. + */ + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + const { scroll, options } = node; + if (node !== this.root && scroll && options.layoutScroll) { + /** + * If this is a new scroll root, we want to remove all previous scrolls + * from the viewport box. + */ + if (scroll.wasRoot) { + copyBoxInto(boxWithoutScroll, box); + } + translateAxis(boxWithoutScroll.x, scroll.offset.x); + translateAxis(boxWithoutScroll.y, scroll.offset.y); + } + } + return boxWithoutScroll; + } + applyTransform(box, transformOnly = false) { + const withTransforms = createBox(); + copyBoxInto(withTransforms, box); + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + if (!transformOnly && + node.options.layoutScroll && + node.scroll && + node !== node.root) { + transformBox(withTransforms, { + x: -node.scroll.offset.x, + y: -node.scroll.offset.y, + }); + } + if (!hasTransform(node.latestValues)) + continue; + transformBox(withTransforms, node.latestValues); + } + if (hasTransform(this.latestValues)) { + transformBox(withTransforms, this.latestValues); + } + return withTransforms; + } + removeTransform(box) { + const boxWithoutTransform = createBox(); + copyBoxInto(boxWithoutTransform, box); + for (let i = 0; i < this.path.length; i++) { + const node = this.path[i]; + if (!node.instance) + continue; + if (!hasTransform(node.latestValues)) + continue; + hasScale(node.latestValues) && node.updateSnapshot(); + const sourceBox = createBox(); + const nodeBox = node.measurePageBox(); + copyBoxInto(sourceBox, nodeBox); + removeBoxTransforms(boxWithoutTransform, node.latestValues, node.snapshot ? node.snapshot.layoutBox : undefined, sourceBox); + } + if (hasTransform(this.latestValues)) { + removeBoxTransforms(boxWithoutTransform, this.latestValues); + } + return boxWithoutTransform; + } + setTargetDelta(delta) { + this.targetDelta = delta; + this.root.scheduleUpdateProjection(); + this.isProjectionDirty = true; + } + setOptions(options) { + this.options = { + ...this.options, + ...options, + crossfade: options.crossfade !== undefined ? options.crossfade : true, + }; + } + clearMeasurements() { + this.scroll = undefined; + this.layout = undefined; + this.snapshot = undefined; + this.prevTransformTemplateValue = undefined; + this.targetDelta = undefined; + this.target = undefined; + this.isLayoutDirty = false; + } + forceRelativeParentToResolveTarget() { + if (!this.relativeParent) + return; + /** + * If the parent target isn't up-to-date, force it to update. + * This is an unfortunate de-optimisation as it means any updating relative + * projection will cause all the relative parents to recalculate back + * up the tree. + */ + if (this.relativeParent.resolvedRelativeTargetAt !== + frameData.timestamp) { + this.relativeParent.resolveTargetDelta(true); + } + } + resolveTargetDelta(forceRecalculation = false) { + /** + * Once the dirty status of nodes has been spread through the tree, we also + * need to check if we have a shared node of a different depth that has itself + * been dirtied. + */ + const lead = this.getLead(); + this.isProjectionDirty || (this.isProjectionDirty = lead.isProjectionDirty); + this.isTransformDirty || (this.isTransformDirty = lead.isTransformDirty); + this.isSharedProjectionDirty || (this.isSharedProjectionDirty = lead.isSharedProjectionDirty); + const isShared = Boolean(this.resumingFrom) || this !== lead; + /** + * We don't use transform for this step of processing so we don't + * need to check whether any nodes have changed transform. + */ + const canSkip = !(forceRecalculation || + (isShared && this.isSharedProjectionDirty) || + this.isProjectionDirty || + this.parent?.isProjectionDirty || + this.attemptToResolveRelativeTarget || + this.root.updateBlockedByResize); + if (canSkip) + return; + const { layout, layoutId } = this.options; + /** + * If we have no layout, we can't perform projection, so early return + */ + if (!this.layout || !(layout || layoutId)) + return; + this.resolvedRelativeTargetAt = frameData.timestamp; + /** + * If we don't have a targetDelta but do have a layout, we can attempt to resolve + * a relativeParent. This will allow a component to perform scale correction + * even if no animation has started. + */ + if (!this.targetDelta && !this.relativeTarget) { + const relativeParent = this.getClosestProjectingParent(); + if (relativeParent && + relativeParent.layout && + this.animationProgress !== 1) { + this.relativeParent = relativeParent; + this.forceRelativeParentToResolveTarget(); + this.relativeTarget = createBox(); + this.relativeTargetOrigin = createBox(); + calcRelativePosition(this.relativeTargetOrigin, this.layout.layoutBox, relativeParent.layout.layoutBox); + copyBoxInto(this.relativeTarget, this.relativeTargetOrigin); + } + else { + this.relativeParent = this.relativeTarget = undefined; + } + } + /** + * If we have no relative target or no target delta our target isn't valid + * for this frame. + */ + if (!this.relativeTarget && !this.targetDelta) + return; + /** + * Lazy-init target data structure + */ + if (!this.target) { + this.target = createBox(); + this.targetWithTransforms = createBox(); + } + /** + * If we've got a relative box for this component, resolve it into a target relative to the parent. + */ + if (this.relativeTarget && + this.relativeTargetOrigin && + this.relativeParent && + this.relativeParent.target) { + this.forceRelativeParentToResolveTarget(); + calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target); + /** + * If we've only got a targetDelta, resolve it into a target + */ + } + else if (this.targetDelta) { + if (Boolean(this.resumingFrom)) { + // TODO: This is creating a new object every frame + this.target = this.applyTransform(this.layout.layoutBox); + } + else { + copyBoxInto(this.target, this.layout.layoutBox); + } + applyBoxDelta(this.target, this.targetDelta); + } + else { + /** + * If no target, use own layout as target + */ + copyBoxInto(this.target, this.layout.layoutBox); + } + /** + * If we've been told to attempt to resolve a relative target, do so. + */ + if (this.attemptToResolveRelativeTarget) { + this.attemptToResolveRelativeTarget = false; + const relativeParent = this.getClosestProjectingParent(); + if (relativeParent && + Boolean(relativeParent.resumingFrom) === + Boolean(this.resumingFrom) && + !relativeParent.options.layoutScroll && + relativeParent.target && + this.animationProgress !== 1) { + this.relativeParent = relativeParent; + this.forceRelativeParentToResolveTarget(); + this.relativeTarget = createBox(); + this.relativeTargetOrigin = createBox(); + calcRelativePosition(this.relativeTargetOrigin, this.target, relativeParent.target); + copyBoxInto(this.relativeTarget, this.relativeTargetOrigin); + } + else { + this.relativeParent = this.relativeTarget = undefined; + } + } + /** + * Increase debug counter for resolved target deltas + */ + if (statsBuffer.value) { + metrics.calculatedTargetDeltas++; + } + } + getClosestProjectingParent() { + if (!this.parent || + hasScale(this.parent.latestValues) || + has2DTranslate(this.parent.latestValues)) { + return undefined; + } + if (this.parent.isProjecting()) { + return this.parent; + } + else { + return this.parent.getClosestProjectingParent(); + } + } + isProjecting() { + return Boolean((this.relativeTarget || + this.targetDelta || + this.options.layoutRoot) && + this.layout); + } + calcProjection() { + const lead = this.getLead(); + const isShared = Boolean(this.resumingFrom) || this !== lead; + let canSkip = true; + /** + * If this is a normal layout animation and neither this node nor its nearest projecting + * is dirty then we can't skip. + */ + if (this.isProjectionDirty || this.parent?.isProjectionDirty) { + canSkip = false; + } + /** + * If this is a shared layout animation and this node's shared projection is dirty then + * we can't skip. + */ + if (isShared && + (this.isSharedProjectionDirty || this.isTransformDirty)) { + canSkip = false; + } + /** + * If we have resolved the target this frame we must recalculate the + * projection to ensure it visually represents the internal calculations. + */ + if (this.resolvedRelativeTargetAt === frameData.timestamp) { + canSkip = false; + } + if (canSkip) + return; + const { layout, layoutId } = this.options; + /** + * If this section of the tree isn't animating we can + * delete our target sources for the following frame. + */ + this.isTreeAnimating = Boolean((this.parent && this.parent.isTreeAnimating) || + this.currentAnimation || + this.pendingAnimation); + if (!this.isTreeAnimating) { + this.targetDelta = this.relativeTarget = undefined; + } + if (!this.layout || !(layout || layoutId)) + return; + /** + * Reset the corrected box with the latest values from box, as we're then going + * to perform mutative operations on it. + */ + copyBoxInto(this.layoutCorrected, this.layout.layoutBox); + /** + * Record previous tree scales before updating. + */ + const prevTreeScaleX = this.treeScale.x; + const prevTreeScaleY = this.treeScale.y; + /** + * Apply all the parent deltas to this box to produce the corrected box. This + * is the layout box, as it will appear on screen as a result of the transforms of its parents. + */ + applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared); + /** + * If this layer needs to perform scale correction but doesn't have a target, + * use the layout as the target. + */ + if (lead.layout && + !lead.target && + (this.treeScale.x !== 1 || this.treeScale.y !== 1)) { + lead.target = lead.layout.layoutBox; + lead.targetWithTransforms = createBox(); + } + const { target } = lead; + if (!target) { + /** + * If we don't have a target to project into, but we were previously + * projecting, we want to remove the stored transform and schedule + * a render to ensure the elements reflect the removed transform. + */ + if (this.prevProjectionDelta) { + this.createProjectionDeltas(); + this.scheduleRender(); + } + return; + } + if (!this.projectionDelta || !this.prevProjectionDelta) { + this.createProjectionDeltas(); + } + else { + copyAxisDeltaInto(this.prevProjectionDelta.x, this.projectionDelta.x); + copyAxisDeltaInto(this.prevProjectionDelta.y, this.projectionDelta.y); + } + /** + * Update the delta between the corrected box and the target box before user-set transforms were applied. + * This will allow us to calculate the corrected borderRadius and boxShadow to compensate + * for our layout reprojection, but still allow them to be scaled correctly by the user. + * It might be that to simplify this we may want to accept that user-set scale is also corrected + * and we wouldn't have to keep and calc both deltas, OR we could support a user setting + * to allow people to choose whether these styles are corrected based on just the + * layout reprojection or the final bounding box. + */ + calcBoxDelta(this.projectionDelta, this.layoutCorrected, target, this.latestValues); + if (this.treeScale.x !== prevTreeScaleX || + this.treeScale.y !== prevTreeScaleY || + !axisDeltaEquals(this.projectionDelta.x, this.prevProjectionDelta.x) || + !axisDeltaEquals(this.projectionDelta.y, this.prevProjectionDelta.y)) { + this.hasProjected = true; + this.scheduleRender(); + this.notifyListeners("projectionUpdate", target); + } + /** + * Increase debug counter for recalculated projections + */ + if (statsBuffer.value) { + metrics.calculatedProjections++; + } + } + hide() { + this.isVisible = false; + // TODO: Schedule render + } + show() { + this.isVisible = true; + // TODO: Schedule render + } + scheduleRender(notifyAll = true) { + this.options.visualElement?.scheduleRender(); + if (notifyAll) { + const stack = this.getStack(); + stack && stack.scheduleRender(); + } + if (this.resumingFrom && !this.resumingFrom.instance) { + this.resumingFrom = undefined; + } + } + createProjectionDeltas() { + this.prevProjectionDelta = createDelta(); + this.projectionDelta = createDelta(); + this.projectionDeltaWithTransform = createDelta(); + } + setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) { + const snapshot = this.snapshot; + const snapshotLatestValues = snapshot ? snapshot.latestValues : {}; + const mixedValues = { ...this.latestValues }; + const targetDelta = createDelta(); + if (!this.relativeParent || + !this.relativeParent.options.layoutRoot) { + this.relativeTarget = this.relativeTargetOrigin = undefined; + } + this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged; + const relativeLayout = createBox(); + const snapshotSource = snapshot ? snapshot.source : undefined; + const layoutSource = this.layout ? this.layout.source : undefined; + const isSharedLayoutAnimation = snapshotSource !== layoutSource; + const stack = this.getStack(); + const isOnlyMember = !stack || stack.members.length <= 1; + const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation && + !isOnlyMember && + this.options.crossfade === true && + !this.path.some(hasOpacityCrossfade)); + this.animationProgress = 0; + let prevRelativeTarget; + this.mixTargetDelta = (latest) => { + const progress = latest / 1000; + mixAxisDelta(targetDelta.x, delta.x, progress); + mixAxisDelta(targetDelta.y, delta.y, progress); + this.setTargetDelta(targetDelta); + if (this.relativeTarget && + this.relativeTargetOrigin && + this.layout && + this.relativeParent && + this.relativeParent.layout) { + calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox); + mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress); + /** + * If this is an unchanged relative target we can consider the + * projection not dirty. + */ + if (prevRelativeTarget && + boxEquals(this.relativeTarget, prevRelativeTarget)) { + this.isProjectionDirty = false; + } + if (!prevRelativeTarget) + prevRelativeTarget = createBox(); + copyBoxInto(prevRelativeTarget, this.relativeTarget); + } + if (isSharedLayoutAnimation) { + this.animationValues = mixedValues; + mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember); + } + this.root.scheduleUpdateProjection(); + this.scheduleRender(); + this.animationProgress = progress; + }; + this.mixTargetDelta(this.options.layoutRoot ? 1000 : 0); + } + startAnimation(options) { + this.notifyListeners("animationStart"); + this.currentAnimation?.stop(); + this.resumingFrom?.currentAnimation?.stop(); + if (this.pendingAnimation) { + cancelFrame(this.pendingAnimation); + this.pendingAnimation = undefined; + } + /** + * Start the animation in the next frame to have a frame with progress 0, + * where the target is the same as when the animation started, so we can + * calculate the relative positions correctly for instant transitions. + */ + this.pendingAnimation = frame.update(() => { + globalProjectionState.hasAnimatedSinceResize = true; + activeAnimations.layout++; + this.motionValue || (this.motionValue = motionValue(0)); + this.currentAnimation = animateSingleValue(this.motionValue, [0, 1000], { + ...options, + velocity: 0, + isSync: true, + onUpdate: (latest) => { + this.mixTargetDelta(latest); + options.onUpdate && options.onUpdate(latest); + }, + onStop: () => { + activeAnimations.layout--; + }, + onComplete: () => { + activeAnimations.layout--; + options.onComplete && options.onComplete(); + this.completeAnimation(); + }, + }); + if (this.resumingFrom) { + this.resumingFrom.currentAnimation = this.currentAnimation; + } + this.pendingAnimation = undefined; + }); + } + completeAnimation() { + if (this.resumingFrom) { + this.resumingFrom.currentAnimation = undefined; + this.resumingFrom.preserveOpacity = undefined; + } + const stack = this.getStack(); + stack && stack.exitAnimationComplete(); + this.resumingFrom = + this.currentAnimation = + this.animationValues = + undefined; + this.notifyListeners("animationComplete"); + } + finishAnimation() { + if (this.currentAnimation) { + this.mixTargetDelta && this.mixTargetDelta(animationTarget); + this.currentAnimation.stop(); + } + this.completeAnimation(); + } + applyTransformsToTarget() { + const lead = this.getLead(); + let { targetWithTransforms, target, layout, latestValues } = lead; + if (!targetWithTransforms || !target || !layout) + return; + /** + * If we're only animating position, and this element isn't the lead element, + * then instead of projecting into the lead box we instead want to calculate + * a new target that aligns the two boxes but maintains the layout shape. + */ + if (this !== lead && + this.layout && + layout && + shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) { + target = this.target || createBox(); + const xLength = calcLength(this.layout.layoutBox.x); + target.x.min = lead.target.x.min; + target.x.max = target.x.min + xLength; + const yLength = calcLength(this.layout.layoutBox.y); + target.y.min = lead.target.y.min; + target.y.max = target.y.min + yLength; + } + copyBoxInto(targetWithTransforms, target); + /** + * Apply the latest user-set transforms to the targetBox to produce the targetBoxFinal. + * This is the final box that we will then project into by calculating a transform delta and + * applying it to the corrected box. + */ + transformBox(targetWithTransforms, latestValues); + /** + * Update the delta between the corrected box and the final target box, after + * user-set transforms are applied to it. This will be used by the renderer to + * create a transform style that will reproject the element from its layout layout + * into the desired bounding box. + */ + calcBoxDelta(this.projectionDeltaWithTransform, this.layoutCorrected, targetWithTransforms, latestValues); + } + registerSharedNode(layoutId, node) { + if (!this.sharedNodes.has(layoutId)) { + this.sharedNodes.set(layoutId, new NodeStack()); + } + const stack = this.sharedNodes.get(layoutId); + stack.add(node); + const config = node.options.initialPromotionConfig; + node.promote({ + transition: config ? config.transition : undefined, + preserveFollowOpacity: config && config.shouldPreserveFollowOpacity + ? config.shouldPreserveFollowOpacity(node) + : undefined, + }); + } + isLead() { + const stack = this.getStack(); + return stack ? stack.lead === this : true; + } + getLead() { + const { layoutId } = this.options; + return layoutId ? this.getStack()?.lead || this : this; + } + getPrevLead() { + const { layoutId } = this.options; + return layoutId ? this.getStack()?.prevLead : undefined; + } + getStack() { + const { layoutId } = this.options; + if (layoutId) + return this.root.sharedNodes.get(layoutId); + } + promote({ needsReset, transition, preserveFollowOpacity, } = {}) { + const stack = this.getStack(); + if (stack) + stack.promote(this, preserveFollowOpacity); + if (needsReset) { + this.projectionDelta = undefined; + this.needsReset = true; + } + if (transition) + this.setOptions({ transition }); + } + relegate() { + const stack = this.getStack(); + if (stack) { + return stack.relegate(this); + } + else { + return false; + } + } + resetSkewAndRotation() { + const { visualElement } = this.options; + if (!visualElement) + return; + // If there's no detected skew or rotation values, we can early return without a forced render. + let hasDistortingTransform = false; + /** + * An unrolled check for rotation values. Most elements don't have any rotation and + * skipping the nested loop and new object creation is 50% faster. + */ + const { latestValues } = visualElement; + if (latestValues.z || + latestValues.rotate || + latestValues.rotateX || + latestValues.rotateY || + latestValues.rotateZ || + latestValues.skewX || + latestValues.skewY) { + hasDistortingTransform = true; + } + // If there's no distorting values, we don't need to do any more. + if (!hasDistortingTransform) + return; + const resetValues = {}; + if (latestValues.z) { + resetDistortingTransform("z", visualElement, resetValues, this.animationValues); + } + // Check the skew and rotate value of all axes and reset to 0 + for (let i = 0; i < transformAxes.length; i++) { + resetDistortingTransform(`rotate${transformAxes[i]}`, visualElement, resetValues, this.animationValues); + resetDistortingTransform(`skew${transformAxes[i]}`, visualElement, resetValues, this.animationValues); + } + // Force a render of this element to apply the transform with all skews and rotations + // set to 0. + visualElement.render(); + // Put back all the values we reset + for (const key in resetValues) { + visualElement.setStaticValue(key, resetValues[key]); + if (this.animationValues) { + this.animationValues[key] = resetValues[key]; + } + } + // Schedule a render for the next frame. This ensures we won't visually + // see the element with the reset rotate value applied. + visualElement.scheduleRender(); + } + applyProjectionStyles(targetStyle, // CSSStyleDeclaration - doesn't allow numbers to be assigned to properties + styleProp) { + if (!this.instance || this.isSVG) + return; + if (!this.isVisible) { + targetStyle.visibility = "hidden"; + return; + } + const transformTemplate = this.getTransformTemplate(); + if (this.needsReset) { + this.needsReset = false; + targetStyle.visibility = ""; + targetStyle.opacity = ""; + targetStyle.pointerEvents = + resolveMotionValue(styleProp?.pointerEvents) || ""; + targetStyle.transform = transformTemplate + ? transformTemplate(this.latestValues, "") + : "none"; + return; + } + const lead = this.getLead(); + if (!this.projectionDelta || !this.layout || !lead.target) { + if (this.options.layoutId) { + targetStyle.opacity = + this.latestValues.opacity !== undefined + ? this.latestValues.opacity + : 1; + targetStyle.pointerEvents = + resolveMotionValue(styleProp?.pointerEvents) || ""; + } + if (this.hasProjected && !hasTransform(this.latestValues)) { + targetStyle.transform = transformTemplate + ? transformTemplate({}, "") + : "none"; + this.hasProjected = false; + } + return; + } + targetStyle.visibility = ""; + const valuesToRender = lead.animationValues || lead.latestValues; + this.applyTransformsToTarget(); + let transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender); + if (transformTemplate) { + transform = transformTemplate(valuesToRender, transform); + } + targetStyle.transform = transform; + const { x, y } = this.projectionDelta; + targetStyle.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`; + if (lead.animationValues) { + /** + * If the lead component is animating, assign this either the entering/leaving + * opacity + */ + targetStyle.opacity = + lead === this + ? valuesToRender.opacity ?? + this.latestValues.opacity ?? + 1 + : this.preserveOpacity + ? this.latestValues.opacity + : valuesToRender.opacityExit; + } + else { + /** + * Or we're not animating at all, set the lead component to its layout + * opacity and other components to hidden. + */ + targetStyle.opacity = + lead === this + ? valuesToRender.opacity !== undefined + ? valuesToRender.opacity + : "" + : valuesToRender.opacityExit !== undefined + ? valuesToRender.opacityExit + : 0; + } + /** + * Apply scale correction + */ + for (const key in scaleCorrectors) { + if (valuesToRender[key] === undefined) + continue; + const { correct, applyTo, isCSSVariable } = scaleCorrectors[key]; + /** + * Only apply scale correction to the value if we have an + * active projection transform. Otherwise these values become + * vulnerable to distortion if the element changes size without + * a corresponding layout animation. + */ + const corrected = transform === "none" + ? valuesToRender[key] + : correct(valuesToRender[key], lead); + if (applyTo) { + const num = applyTo.length; + for (let i = 0; i < num; i++) { + targetStyle[applyTo[i]] = corrected; + } + } + else { + // If this is a CSS variable, set it directly on the instance. + // Replacing this function from creating styles to setting them + // would be a good place to remove per frame object creation + if (isCSSVariable) { + this.options.visualElement.renderState.vars[key] = corrected; + } + else { + targetStyle[key] = corrected; + } + } + } + /** + * Disable pointer events on follow components. This is to ensure + * that if a follow component covers a lead component it doesn't block + * pointer events on the lead. + */ + if (this.options.layoutId) { + targetStyle.pointerEvents = + lead === this + ? resolveMotionValue(styleProp?.pointerEvents) || "" + : "none"; + } + } + clearSnapshot() { + this.resumeFrom = this.snapshot = undefined; + } + // Only run on root + resetTree() { + this.root.nodes.forEach((node) => node.currentAnimation?.stop()); + this.root.nodes.forEach(clearMeasurements); + this.root.sharedNodes.clear(); + } + }; + } + function updateLayout(node) { + node.updateLayout(); + } + function notifyLayoutUpdate(node) { + const snapshot = node.resumeFrom?.snapshot || node.snapshot; + if (node.isLead() && + node.layout && + snapshot && + node.hasListeners("didUpdate")) { + const { layoutBox: layout, measuredBox: measuredLayout } = node.layout; + const { animationType } = node.options; + const isShared = snapshot.source !== node.layout.source; + // TODO Maybe we want to also resize the layout snapshot so we don't trigger + // animations for instance if layout="size" and an element has only changed position + if (animationType === "size") { + eachAxis((axis) => { + const axisSnapshot = isShared + ? snapshot.measuredBox[axis] + : snapshot.layoutBox[axis]; + const length = calcLength(axisSnapshot); + axisSnapshot.min = layout[axis].min; + axisSnapshot.max = axisSnapshot.min + length; + }); + } + else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) { + eachAxis((axis) => { + const axisSnapshot = isShared + ? snapshot.measuredBox[axis] + : snapshot.layoutBox[axis]; + const length = calcLength(layout[axis]); + axisSnapshot.max = axisSnapshot.min + length; + /** + * Ensure relative target gets resized and rerendererd + */ + if (node.relativeTarget && !node.currentAnimation) { + node.isProjectionDirty = true; + node.relativeTarget[axis].max = + node.relativeTarget[axis].min + length; + } + }); + } + const layoutDelta = createDelta(); + calcBoxDelta(layoutDelta, layout, snapshot.layoutBox); + const visualDelta = createDelta(); + if (isShared) { + calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox); + } + else { + calcBoxDelta(visualDelta, layout, snapshot.layoutBox); + } + const hasLayoutChanged = !isDeltaZero(layoutDelta); + let hasRelativeLayoutChanged = false; + if (!node.resumeFrom) { + const relativeParent = node.getClosestProjectingParent(); + /** + * If the relativeParent is itself resuming from a different element then + * the relative snapshot is not relavent + */ + if (relativeParent && !relativeParent.resumeFrom) { + const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent; + if (parentSnapshot && parentLayout) { + const relativeSnapshot = createBox(); + calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox); + const relativeLayout = createBox(); + calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox); + if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) { + hasRelativeLayoutChanged = true; + } + if (relativeParent.options.layoutRoot) { + node.relativeTarget = relativeLayout; + node.relativeTargetOrigin = relativeSnapshot; + node.relativeParent = relativeParent; + } + } + } + } + node.notifyListeners("didUpdate", { + layout, + snapshot, + delta: visualDelta, + layoutDelta, + hasLayoutChanged, + hasRelativeLayoutChanged, + }); + } + else if (node.isLead()) { + const { onExitComplete } = node.options; + onExitComplete && onExitComplete(); + } + /** + * Clearing transition + * TODO: Investigate why this transition is being passed in as {type: false } from Framer + * and why we need it at all + */ + node.options.transition = undefined; + } + function propagateDirtyNodes(node) { + /** + * Increase debug counter for nodes encountered this frame + */ + if (statsBuffer.value) { + metrics.nodes++; + } + if (!node.parent) + return; + /** + * If this node isn't projecting, propagate isProjectionDirty. It will have + * no performance impact but it will allow the next child that *is* projecting + * but *isn't* dirty to just check its parent to see if *any* ancestor needs + * correcting. + */ + if (!node.isProjecting()) { + node.isProjectionDirty = node.parent.isProjectionDirty; + } + /** + * Propagate isSharedProjectionDirty and isTransformDirty + * throughout the whole tree. A future revision can take another look at + * this but for safety we still recalcualte shared nodes. + */ + node.isSharedProjectionDirty || (node.isSharedProjectionDirty = Boolean(node.isProjectionDirty || + node.parent.isProjectionDirty || + node.parent.isSharedProjectionDirty)); + node.isTransformDirty || (node.isTransformDirty = node.parent.isTransformDirty); + } + function cleanDirtyNodes(node) { + node.isProjectionDirty = + node.isSharedProjectionDirty = + node.isTransformDirty = + false; + } + function clearSnapshot(node) { + node.clearSnapshot(); + } + function clearMeasurements(node) { + node.clearMeasurements(); + } + function clearIsLayoutDirty(node) { + node.isLayoutDirty = false; + } + function resetTransformStyle(node) { + const { visualElement } = node.options; + if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) { + visualElement.notify("BeforeLayoutMeasure"); + } + node.resetTransform(); + } + function finishAnimation(node) { + node.finishAnimation(); + node.targetDelta = node.relativeTarget = node.target = undefined; + node.isProjectionDirty = true; + } + function resolveTargetDelta(node) { + node.resolveTargetDelta(); + } + function calcProjection(node) { + node.calcProjection(); + } + function resetSkewAndRotation(node) { + node.resetSkewAndRotation(); + } + function removeLeadSnapshots(stack) { + stack.removeLeadSnapshot(); + } + function mixAxisDelta(output, delta, p) { + output.translate = mixNumber$1(delta.translate, 0, p); + output.scale = mixNumber$1(delta.scale, 1, p); + output.origin = delta.origin; + output.originPoint = delta.originPoint; + } + function mixAxis(output, from, to, p) { + output.min = mixNumber$1(from.min, to.min, p); + output.max = mixNumber$1(from.max, to.max, p); + } + function mixBox(output, from, to, p) { + mixAxis(output.x, from.x, to.x, p); + mixAxis(output.y, from.y, to.y, p); + } + function hasOpacityCrossfade(node) { + return (node.animationValues && node.animationValues.opacityExit !== undefined); + } + const defaultLayoutTransition = { + duration: 0.45, + ease: [0.4, 0, 0.1, 1], + }; + const userAgentContains = (string) => typeof navigator !== "undefined" && + navigator.userAgent && + navigator.userAgent.toLowerCase().includes(string); + /** + * Measured bounding boxes must be rounded in Safari and + * left untouched in Chrome, otherwise non-integer layouts within scaled-up elements + * can appear to jump. + */ + const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/") + ? Math.round + : noop; + function roundAxis(axis) { + // Round to the nearest .5 pixels to support subpixel layouts + axis.min = roundPoint(axis.min); + axis.max = roundPoint(axis.max); + } + function roundBox(box) { + roundAxis(box.x); + roundAxis(box.y); + } + function shouldAnimatePositionOnly(animationType, snapshot, layout) { + return (animationType === "position" || + (animationType === "preserve-aspect" && + !isNear(aspectRatio(snapshot), aspectRatio(layout), 0.2))); + } + function checkNodeWasScrollRoot(node) { + return node !== node.root && node.scroll?.wasRoot; + } + + function addDomEvent(target, eventName, handler, options = { passive: true }) { + target.addEventListener(eventName, handler, options); + return () => target.removeEventListener(eventName, handler); + } + + const DocumentProjectionNode = createProjectionNode$1({ + attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify), + measureScroll: () => ({ + x: document.documentElement.scrollLeft || document.body.scrollLeft, + y: document.documentElement.scrollTop || document.body.scrollTop, + }), + checkIsScrollRoot: () => true, + }); + + const rootProjectionNode = { + current: undefined, + }; + const HTMLProjectionNode = createProjectionNode$1({ + measureScroll: (instance) => ({ + x: instance.scrollLeft, + y: instance.scrollTop, + }), + defaultParent: () => { + if (!rootProjectionNode.current) { + const documentNode = new DocumentProjectionNode({}); + documentNode.mount(window); + documentNode.setOptions({ layoutScroll: true }); + rootProjectionNode.current = documentNode; + } + return rootProjectionNode.current; + }, + resetTransform: (instance, value) => { + instance.style.transform = value !== undefined ? value : "none"; + }, + checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"), + }); + + function pixelsToPercent(pixels, axis) { + if (axis.max === axis.min) + return 0; + return (pixels / (axis.max - axis.min)) * 100; + } + /** + * We always correct borderRadius as a percentage rather than pixels to reduce paints. + * For example, if you are projecting a box that is 100px wide with a 10px borderRadius + * into a box that is 200px wide with a 20px borderRadius, that is actually a 10% + * borderRadius in both states. If we animate between the two in pixels that will trigger + * a paint each time. If we animate between the two in percentage we'll avoid a paint. + */ + const correctBorderRadius = { + correct: (latest, node) => { + if (!node.target) + return latest; + /** + * If latest is a string, if it's a percentage we can return immediately as it's + * going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number. + */ + if (typeof latest === "string") { + if (px.test(latest)) { + latest = parseFloat(latest); + } + else { + return latest; + } + } + /** + * If latest is a number, it's a pixel value. We use the current viewportBox to calculate that + * pixel value as a percentage of each axis + */ + const x = pixelsToPercent(latest, node.target.x); + const y = pixelsToPercent(latest, node.target.y); + return `${x}% ${y}%`; + }, + }; + + const correctBoxShadow = { + correct: (latest, { treeScale, projectionDelta }) => { + const original = latest; + const shadow = complex.parse(latest); + // TODO: Doesn't support multiple shadows + if (shadow.length > 5) + return original; + const template = complex.createTransformer(latest); + const offset = typeof shadow[0] !== "number" ? 1 : 0; + // Calculate the overall context scale + const xScale = projectionDelta.x.scale * treeScale.x; + const yScale = projectionDelta.y.scale * treeScale.y; + shadow[0 + offset] /= xScale; + shadow[1 + offset] /= yScale; + /** + * Ideally we'd correct x and y scales individually, but because blur and + * spread apply to both we have to take a scale average and apply that instead. + * We could potentially improve the outcome of this by incorporating the ratio between + * the two scales. + */ + const averageScale = mixNumber$1(xScale, yScale, 0.5); + // Blur + if (typeof shadow[2 + offset] === "number") + shadow[2 + offset] /= averageScale; + // Spread + if (typeof shadow[3 + offset] === "number") + shadow[3 + offset] /= averageScale; + return template(shadow); + }, + }; + + /** + * Bounding boxes tend to be defined as top, left, right, bottom. For various operations + * it's easier to consider each axis individually. This function returns a bounding box + * as a map of single-axis min/max values. + */ + function convertBoundingBoxToBox({ top, left, right, bottom, }) { + return { + x: { min: left, max: right }, + y: { min: top, max: bottom }, + }; + } + function convertBoxToBoundingBox({ x, y }) { + return { top: y.min, right: x.max, bottom: y.max, left: x.min }; + } + /** + * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function + * provided by Framer to allow measured points to be corrected for device scaling. This is used + * when measuring DOM elements and DOM event points. + */ + function transformBoxPoints(point, transformPoint) { + if (!transformPoint) + return point; + const topLeft = transformPoint({ x: point.left, y: point.top }); + const bottomRight = transformPoint({ x: point.right, y: point.bottom }); + return { + top: topLeft.y, + left: topLeft.x, + bottom: bottomRight.y, + right: bottomRight.x, + }; + } + + function measureViewportBox(instance, transformPoint) { + return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint)); + } + function measurePageBox(element, rootProjectionNode, transformPagePoint) { + const viewportBox = measureViewportBox(element, transformPagePoint); + const { scroll } = rootProjectionNode; + if (scroll) { + translateAxis(viewportBox.x, scroll.offset.x); + translateAxis(viewportBox.y, scroll.offset.y); + } + return viewportBox; + } + + const featureProps = { + animation: [ + "animate", + "variants", + "whileHover", + "whileTap", + "exit", + "whileInView", + "whileFocus", + "whileDrag", + ], + exit: ["exit"], + drag: ["drag", "dragControls"], + focus: ["whileFocus"], + hover: ["whileHover", "onHoverStart", "onHoverEnd"], + tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"], + pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"], + inView: ["whileInView", "onViewportEnter", "onViewportLeave"], + layout: ["layout", "layoutId"], + }; + const featureDefinitions = {}; + for (const key in featureProps) { + featureDefinitions[key] = { + isEnabled: (props) => featureProps[key].some((name) => !!props[name]), + }; + } + + // Does this device prefer reduced motion? Returns `null` server-side. + const prefersReducedMotion = { current: null }; + const hasReducedMotionListener = { current: false }; + + function initPrefersReducedMotion() { + hasReducedMotionListener.current = true; + if (!isBrowser) + return; + if (window.matchMedia) { + const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)"); + const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches); + motionMediaQuery.addEventListener("change", setReducedMotionPreferences); + setReducedMotionPreferences(); + } + else { + prefersReducedMotion.current = false; + } + } + + const visualElementStore = new WeakMap(); + + function isAnimationControls(v) { + return (v !== null && + typeof v === "object" && + typeof v.start === "function"); + } + + /** + * Decides if the supplied variable is variant label + */ + function isVariantLabel(v) { + return typeof v === "string" || Array.isArray(v); + } + + const variantPriorityOrder = [ + "animate", + "whileInView", + "whileFocus", + "whileHover", + "whileTap", + "whileDrag", + "exit", + ]; + const variantProps = ["initial", ...variantPriorityOrder]; + + function isControllingVariants(props) { + return (isAnimationControls(props.animate) || + variantProps.some((name) => isVariantLabel(props[name]))); + } + function isVariantNode(props) { + return Boolean(isControllingVariants(props) || props.variants); + } + + function updateMotionValuesFromProps(element, next, prev) { + for (const key in next) { + const nextValue = next[key]; + const prevValue = prev[key]; + if (isMotionValue(nextValue)) { + /** + * If this is a motion value found in props or style, we want to add it + * to our visual element's motion value map. + */ + element.addValue(key, nextValue); + } + else if (isMotionValue(prevValue)) { + /** + * If we're swapping from a motion value to a static value, + * create a new motion value from that + */ + element.addValue(key, motionValue(nextValue, { owner: element })); + } + else if (prevValue !== nextValue) { + /** + * If this is a flat value that has changed, update the motion value + * or create one if it doesn't exist. We only want to do this if we're + * not handling the value with our animation state. + */ + if (element.hasValue(key)) { + const existingValue = element.getValue(key); + if (existingValue.liveStyle === true) { + existingValue.jump(nextValue); + } + else if (!existingValue.hasAnimated) { + existingValue.set(nextValue); + } + } + else { + const latestValue = element.getStaticValue(key); + element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element })); + } + } + } + // Handle removed values + for (const key in prev) { + if (next[key] === undefined) + element.removeValue(key); + } + return next; + } + + function getValueState(visualElement) { + const state = [{}, {}]; + visualElement?.values.forEach((value, key) => { + state[0][key] = value.get(); + state[1][key] = value.getVelocity(); + }); + return state; + } + function resolveVariantFromProps(props, definition, custom, visualElement) { + /** + * If the variant definition is a function, resolve. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + /** + * If the variant definition is a variant label, or + * the function returned a variant label, resolve. + */ + if (typeof definition === "string") { + definition = props.variants && props.variants[definition]; + } + /** + * At this point we've resolved both functions and variant labels, + * but the resolved variant label might itself have been a function. + * If so, resolve. This can only have returned a valid target object. + */ + if (typeof definition === "function") { + const [current, velocity] = getValueState(visualElement); + definition = definition(custom !== undefined ? custom : props.custom, current, velocity); + } + return definition; + } + + const propEventHandlers = [ + "AnimationStart", + "AnimationComplete", + "Update", + "BeforeLayoutMeasure", + "LayoutMeasure", + "LayoutAnimationStart", + "LayoutAnimationComplete", + ]; + /** + * A VisualElement is an imperative abstraction around UI elements such as + * HTMLElement, SVGElement, Three.Object3D etc. + */ + class VisualElement { + /** + * This method takes React props and returns found MotionValues. For example, HTML + * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays. + * + * This isn't an abstract method as it needs calling in the constructor, but it is + * intended to be one. + */ + scrapeMotionValuesFromProps(_props, _prevProps, _visualElement) { + return {}; + } + constructor({ parent, props, presenceContext, reducedMotionConfig, blockInitialAnimation, visualState, }, options = {}) { + /** + * A reference to the current underlying Instance, e.g. a HTMLElement + * or Three.Mesh etc. + */ + this.current = null; + /** + * A set containing references to this VisualElement's children. + */ + this.children = new Set(); + /** + * Determine what role this visual element should take in the variant tree. + */ + this.isVariantNode = false; + this.isControllingVariants = false; + /** + * Decides whether this VisualElement should animate in reduced motion + * mode. + * + * TODO: This is currently set on every individual VisualElement but feels + * like it could be set globally. + */ + this.shouldReduceMotion = null; + /** + * A map of all motion values attached to this visual element. Motion + * values are source of truth for any given animated value. A motion + * value might be provided externally by the component via props. + */ + this.values = new Map(); + this.KeyframeResolver = KeyframeResolver; + /** + * Cleanup functions for active features (hover/tap/exit etc) + */ + this.features = {}; + /** + * A map of every subscription that binds the provided or generated + * motion values onChange listeners to this visual element. + */ + this.valueSubscriptions = new Map(); + /** + * A reference to the previously-provided motion values as returned + * from scrapeMotionValuesFromProps. We use the keys in here to determine + * if any motion values need to be removed after props are updated. + */ + this.prevMotionValues = {}; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + /** + * An object containing an unsubscribe function for each prop event subscription. + * For example, every "Update" event can have multiple subscribers via + * VisualElement.on(), but only one of those can be defined via the onUpdate prop. + */ + this.propEventSubscriptions = {}; + this.notifyUpdate = () => this.notify("Update", this.latestValues); + this.render = () => { + if (!this.current) + return; + this.triggerBuild(); + this.renderInstance(this.current, this.renderState, this.props.style, this.projection); + }; + this.renderScheduledAt = 0.0; + this.scheduleRender = () => { + const now = time.now(); + if (this.renderScheduledAt < now) { + this.renderScheduledAt = now; + frame.render(this.render, false, true); + } + }; + const { latestValues, renderState } = visualState; + this.latestValues = latestValues; + this.baseTarget = { ...latestValues }; + this.initialValues = props.initial ? { ...latestValues } : {}; + this.renderState = renderState; + this.parent = parent; + this.props = props; + this.presenceContext = presenceContext; + this.depth = parent ? parent.depth + 1 : 0; + this.reducedMotionConfig = reducedMotionConfig; + this.options = options; + this.blockInitialAnimation = Boolean(blockInitialAnimation); + this.isControllingVariants = isControllingVariants(props); + this.isVariantNode = isVariantNode(props); + if (this.isVariantNode) { + this.variantChildren = new Set(); + } + this.manuallyAnimateOnMount = Boolean(parent && parent.current); + /** + * Any motion values that are provided to the element when created + * aren't yet bound to the element, as this would technically be impure. + * However, we iterate through the motion values and set them to the + * initial values for this component. + * + * TODO: This is impure and we should look at changing this to run on mount. + * Doing so will break some tests but this isn't necessarily a breaking change, + * more a reflection of the test. + */ + const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {}, this); + for (const key in initialMotionValues) { + const value = initialMotionValues[key]; + if (latestValues[key] !== undefined && isMotionValue(value)) { + value.set(latestValues[key], false); + } + } + } + mount(instance) { + this.current = instance; + visualElementStore.set(instance, this); + if (this.projection && !this.projection.instance) { + this.projection.mount(instance); + } + if (this.parent && this.isVariantNode && !this.isControllingVariants) { + this.removeFromVariantTree = this.parent.addVariantChild(this); + } + this.values.forEach((value, key) => this.bindToMotionValue(key, value)); + if (!hasReducedMotionListener.current) { + initPrefersReducedMotion(); + } + this.shouldReduceMotion = + this.reducedMotionConfig === "never" + ? false + : this.reducedMotionConfig === "always" + ? true + : prefersReducedMotion.current; + { + warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + if (this.parent) + this.parent.children.add(this); + this.update(this.props, this.presenceContext); + } + unmount() { + this.projection && this.projection.unmount(); + cancelFrame(this.notifyUpdate); + cancelFrame(this.render); + this.valueSubscriptions.forEach((remove) => remove()); + this.valueSubscriptions.clear(); + this.removeFromVariantTree && this.removeFromVariantTree(); + this.parent && this.parent.children.delete(this); + for (const key in this.events) { + this.events[key].clear(); + } + for (const key in this.features) { + const feature = this.features[key]; + if (feature) { + feature.unmount(); + feature.isMounted = false; + } + } + this.current = null; + } + bindToMotionValue(key, value) { + if (this.valueSubscriptions.has(key)) { + this.valueSubscriptions.get(key)(); + } + const valueIsTransform = transformProps.has(key); + if (valueIsTransform && this.onBindTransform) { + this.onBindTransform(); + } + const removeOnChange = value.on("change", (latestValue) => { + this.latestValues[key] = latestValue; + this.props.onUpdate && frame.preRender(this.notifyUpdate); + if (valueIsTransform && this.projection) { + this.projection.isTransformDirty = true; + } + }); + const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender); + let removeSyncCheck; + if (window.MotionCheckAppearSync) { + removeSyncCheck = window.MotionCheckAppearSync(this, key, value); + } + this.valueSubscriptions.set(key, () => { + removeOnChange(); + removeOnRenderRequest(); + if (removeSyncCheck) + removeSyncCheck(); + if (value.owner) + value.stop(); + }); + } + sortNodePosition(other) { + /** + * If these nodes aren't even of the same type we can't compare their depth. + */ + if (!this.current || + !this.sortInstanceNodePosition || + this.type !== other.type) { + return 0; + } + return this.sortInstanceNodePosition(this.current, other.current); + } + updateFeatures() { + let key = "animation"; + for (key in featureDefinitions) { + const featureDefinition = featureDefinitions[key]; + if (!featureDefinition) + continue; + const { isEnabled, Feature: FeatureConstructor } = featureDefinition; + /** + * If this feature is enabled but not active, make a new instance. + */ + if (!this.features[key] && + FeatureConstructor && + isEnabled(this.props)) { + this.features[key] = new FeatureConstructor(this); + } + /** + * If we have a feature, mount or update it. + */ + if (this.features[key]) { + const feature = this.features[key]; + if (feature.isMounted) { + feature.update(); + } + else { + feature.mount(); + feature.isMounted = true; + } + } + } + } + triggerBuild() { + this.build(this.renderState, this.latestValues, this.props); + } + /** + * Measure the current viewport box with or without transforms. + * Only measures axis-aligned boxes, rotate and skew must be manually + * removed with a re-render to work. + */ + measureViewportBox() { + return this.current + ? this.measureInstanceViewportBox(this.current, this.props) + : createBox(); + } + getStaticValue(key) { + return this.latestValues[key]; + } + setStaticValue(key, value) { + this.latestValues[key] = value; + } + /** + * Update the provided props. Ensure any newly-added motion values are + * added to our map, old ones removed, and listeners updated. + */ + update(props, presenceContext) { + if (props.transformTemplate || this.props.transformTemplate) { + this.scheduleRender(); + } + this.prevProps = this.props; + this.props = props; + this.prevPresenceContext = this.presenceContext; + this.presenceContext = presenceContext; + /** + * Update prop event handlers ie onAnimationStart, onAnimationComplete + */ + for (let i = 0; i < propEventHandlers.length; i++) { + const key = propEventHandlers[i]; + if (this.propEventSubscriptions[key]) { + this.propEventSubscriptions[key](); + delete this.propEventSubscriptions[key]; + } + const listenerName = ("on" + key); + const listener = props[listenerName]; + if (listener) { + this.propEventSubscriptions[key] = this.on(key, listener); + } + } + this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps, this), this.prevMotionValues); + if (this.handleChildMotionValue) { + this.handleChildMotionValue(); + } + } + getProps() { + return this.props; + } + /** + * Returns the variant definition with a given name. + */ + getVariant(name) { + return this.props.variants ? this.props.variants[name] : undefined; + } + /** + * Returns the defined default transition on this component. + */ + getDefaultTransition() { + return this.props.transition; + } + getTransformPagePoint() { + return this.props.transformPagePoint; + } + getClosestVariantNode() { + return this.isVariantNode + ? this + : this.parent + ? this.parent.getClosestVariantNode() + : undefined; + } + /** + * Add a child visual element to our set of children. + */ + addVariantChild(child) { + const closestVariantNode = this.getClosestVariantNode(); + if (closestVariantNode) { + closestVariantNode.variantChildren && + closestVariantNode.variantChildren.add(child); + return () => closestVariantNode.variantChildren.delete(child); + } + } + /** + * Add a motion value and bind it to this visual element. + */ + addValue(key, value) { + // Remove existing value if it exists + const existingValue = this.values.get(key); + if (value !== existingValue) { + if (existingValue) + this.removeValue(key); + this.bindToMotionValue(key, value); + this.values.set(key, value); + this.latestValues[key] = value.get(); + } + } + /** + * Remove a motion value and unbind any active subscriptions. + */ + removeValue(key) { + this.values.delete(key); + const unsubscribe = this.valueSubscriptions.get(key); + if (unsubscribe) { + unsubscribe(); + this.valueSubscriptions.delete(key); + } + delete this.latestValues[key]; + this.removeValueFromRenderState(key, this.renderState); + } + /** + * Check whether we have a motion value for this key + */ + hasValue(key) { + return this.values.has(key); + } + getValue(key, defaultValue) { + if (this.props.values && this.props.values[key]) { + return this.props.values[key]; + } + let value = this.values.get(key); + if (value === undefined && defaultValue !== undefined) { + value = motionValue(defaultValue === null ? undefined : defaultValue, { owner: this }); + this.addValue(key, value); + } + return value; + } + /** + * If we're trying to animate to a previously unencountered value, + * we need to check for it in our state and as a last resort read it + * directly from the instance (which might have performance implications). + */ + readValue(key, target) { + let value = this.latestValues[key] !== undefined || !this.current + ? this.latestValues[key] + : this.getBaseTargetFromProps(this.props, key) ?? + this.readValueFromInstance(this.current, key, this.options); + if (value !== undefined && value !== null) { + if (typeof value === "string" && + (isNumericalString(value) || isZeroValueString(value))) { + // If this is a number read as a string, ie "0" or "200", convert it to a number + value = parseFloat(value); + } + else if (!findValueType(value) && complex.test(target)) { + value = getAnimatableNone(key, target); + } + this.setBaseTarget(key, isMotionValue(value) ? value.get() : value); + } + return isMotionValue(value) ? value.get() : value; + } + /** + * Set the base target to later animate back to. This is currently + * only hydrated on creation and when we first read a value. + */ + setBaseTarget(key, value) { + this.baseTarget[key] = value; + } + /** + * Find the base target for a value thats been removed from all animation + * props. + */ + getBaseTarget(key) { + const { initial } = this.props; + let valueFromInitial; + if (typeof initial === "string" || typeof initial === "object") { + const variant = resolveVariantFromProps(this.props, initial, this.presenceContext?.custom); + if (variant) { + valueFromInitial = variant[key]; + } + } + /** + * If this value still exists in the current initial variant, read that. + */ + if (initial && valueFromInitial !== undefined) { + return valueFromInitial; + } + /** + * Alternatively, if this VisualElement config has defined a getBaseTarget + * so we can read the value from an alternative source, try that. + */ + const target = this.getBaseTargetFromProps(this.props, key); + if (target !== undefined && !isMotionValue(target)) + return target; + /** + * If the value was initially defined on initial, but it doesn't any more, + * return undefined. Otherwise return the value as initially read from the DOM. + */ + return this.initialValues[key] !== undefined && + valueFromInitial === undefined + ? undefined + : this.baseTarget[key]; + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new SubscriptionManager(); + } + return this.events[eventName].add(callback); + } + notify(eventName, ...args) { + if (this.events[eventName]) { + this.events[eventName].notify(...args); + } + } + } + + class DOMVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.KeyframeResolver = DOMKeyframesResolver; + } + sortInstanceNodePosition(a, b) { + /** + * compareDocumentPosition returns a bitmask, by using the bitwise & + * we're returning true if 2 in that bitmask is set to true. 2 is set + * to true if b preceeds a. + */ + return a.compareDocumentPosition(b) & 2 ? 1 : -1; + } + getBaseTargetFromProps(props, key) { + return props.style + ? props.style[key] + : undefined; + } + removeValueFromRenderState(key, { vars, style }) { + delete vars[key]; + delete style[key]; + } + handleChildMotionValue() { + if (this.childSubscription) { + this.childSubscription(); + delete this.childSubscription; + } + const { children } = this.props; + if (isMotionValue(children)) { + this.childSubscription = children.on("change", (latest) => { + if (this.current) { + this.current.textContent = `${latest}`; + } + }); + } + } + } + + const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", + }; + const numTransforms = transformPropOrder.length; + /** + * Build a CSS transform style from individual x/y/scale etc properties. + * + * This outputs with a default order of transforms/scales/rotations, this can be customised by + * providing a transformTemplate function. + */ + function buildTransform(latestValues, transform, transformTemplate) { + // The transform string we're going to build into. + let transformString = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < numTransforms; i++) { + const key = transformPropOrder[i]; + const value = latestValues[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault || transformTemplate) { + const valueAsType = getValueAsType(value, numberValueTypes[key]); + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + transformString += `${transformName}(${valueAsType}) `; + } + if (transformTemplate) { + transform[key] = valueAsType; + } + } + } + transformString = transformString.trim(); + // If we have a custom `transform` template, pass our transform values and + // generated transformString to that before returning + if (transformTemplate) { + transformString = transformTemplate(transform, transformIsDefault ? "" : transformString); + } + else if (transformIsDefault) { + transformString = "none"; + } + return transformString; + } + + function buildHTMLStyles(state, latestValues, transformTemplate) { + const { style, vars, transformOrigin } = state; + // Track whether we encounter any transform or transformOrigin values. + let hasTransform = false; + let hasTransformOrigin = false; + /** + * Loop over all our latest animated values and decide whether to handle them + * as a style or CSS variable. + * + * Transforms and transform origins are kept separately for further processing. + */ + for (const key in latestValues) { + const value = latestValues[key]; + if (transformProps.has(key)) { + // If this is a transform, flag to enable further transform processing + hasTransform = true; + continue; + } + else if (isCSSVariableName(key)) { + vars[key] = value; + continue; + } + else { + // Convert the value to its default value type, ie 0 -> "0px" + const valueAsType = getValueAsType(value, numberValueTypes[key]); + if (key.startsWith("origin")) { + // If this is a transform origin, flag and enable further transform-origin processing + hasTransformOrigin = true; + transformOrigin[key] = + valueAsType; + } + else { + style[key] = valueAsType; + } + } + } + if (!latestValues.transform) { + if (hasTransform || transformTemplate) { + style.transform = buildTransform(latestValues, state.transform, transformTemplate); + } + else if (style.transform) { + /** + * If we have previously created a transform but currently don't have any, + * reset transform style to none. + */ + style.transform = "none"; + } + } + /** + * Build a transformOrigin style. Uses the same defaults as the browser for + * undefined origins. + */ + if (hasTransformOrigin) { + const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin; + style.transformOrigin = `${originX} ${originY} ${originZ}`; + } + } + + function renderHTML(element, { style, vars }, styleProp, projection) { + const elementStyle = element.style; + let key; + for (key in style) { + // CSSStyleDeclaration has [index: number]: string; in the types, so we use that as key type. + elementStyle[key] = style[key]; + } + // Write projection styles directly to element style + projection?.applyProjectionStyles(elementStyle, styleProp); + for (key in vars) { + // Loop over any CSS variables and assign those. + // They can only be assigned using `setProperty`. + elementStyle.setProperty(key, vars[key]); + } + } + + function isForcedMotionValue(key, { layout, layoutId }) { + return (transformProps.has(key) || + key.startsWith("origin") || + ((layout || layoutId !== undefined) && + (!!scaleCorrectors[key] || key === "opacity"))); + } + + function scrapeMotionValuesFromProps$1(props, prevProps, visualElement) { + const { style } = props; + const newValues = {}; + for (const key in style) { + if (isMotionValue(style[key]) || + (prevProps.style && + isMotionValue(prevProps.style[key])) || + isForcedMotionValue(key, props) || + visualElement?.getValue(key)?.liveStyle !== undefined) { + newValues[key] = style[key]; + } + } + return newValues; + } + + function getComputedStyle$1(element) { + return window.getComputedStyle(element); + } + class HTMLVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "html"; + this.renderInstance = renderHTML; + } + readValueFromInstance(instance, key) { + if (transformProps.has(key)) { + return this.projection?.isProjecting + ? defaultTransformValue(key) + : readTransformValue(instance, key); + } + else { + const computedStyle = getComputedStyle$1(instance); + const value = (isCSSVariableName(key) + ? computedStyle.getPropertyValue(key) + : computedStyle[key]) || 0; + return typeof value === "string" ? value.trim() : value; + } + } + measureInstanceViewportBox(instance, { transformPagePoint }) { + return measureViewportBox(instance, transformPagePoint); + } + build(renderState, latestValues, props) { + buildHTMLStyles(renderState, latestValues, props.transformTemplate); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + } + } + + function useIsMounted() { + const isMounted = React$1.useRef(false); + useIsomorphicLayoutEffect(() => { + isMounted.current = true; + return () => { + isMounted.current = false; + }; + }, []); + return isMounted; + } + + function useForceUpdate() { + const isMounted = useIsMounted(); + const [forcedRenderCount, setForcedRenderCount] = React$1.useState(0); + const forceRender = React$1.useCallback(() => { + isMounted.current && setForcedRenderCount(forcedRenderCount + 1); + }, [forcedRenderCount]); + /** + * Defer this to the end of the next animation frame in case there are multiple + * synchronous calls. + */ + const deferredForceRender = React$1.useCallback(() => frame.postRender(forceRender), [forceRender]); + return [deferredForceRender, forcedRenderCount]; + } + + const shouldInheritGroup = (inherit) => inherit === true; + const shouldInheritId = (inherit) => shouldInheritGroup(inherit === true) || inherit === "id"; + const LayoutGroup = ({ children, id, inherit = true }) => { + const layoutGroupContext = React$1.useContext(LayoutGroupContext); + const deprecatedLayoutGroupContext = React$1.useContext(DeprecatedLayoutGroupContext); + const [forceRender, key] = useForceUpdate(); + const context = React$1.useRef(null); + const upstreamId = layoutGroupContext.id || deprecatedLayoutGroupContext; + if (context.current === null) { + if (shouldInheritId(inherit) && upstreamId) { + id = id ? upstreamId + "-" + id : upstreamId; + } + context.current = { + id, + group: shouldInheritGroup(inherit) + ? layoutGroupContext.group || nodeGroup() + : nodeGroup(), + }; + } + const memoizedContext = React$1.useMemo(() => ({ ...context.current, forceRender }), [key]); + return (jsx(LayoutGroupContext.Provider, { value: memoizedContext, children: children })); + }; + + const LazyContext = React$1.createContext({ strict: false }); + + function loadFeatures(features) { + for (const key in features) { + featureDefinitions[key] = { + ...featureDefinitions[key], + ...features[key], + }; + } + } + + /** + * Used in conjunction with the `m` component to reduce bundle size. + * + * `m` is a version of the `motion` component that only loads functionality + * critical for the initial render. + * + * `LazyMotion` can then be used to either synchronously or asynchronously + * load animation and gesture support. + * + * ```jsx + * // Synchronous loading + * import { LazyMotion, m, domAnimation } from "framer-motion" + * + * function App() { + * return ( + * + * + * + * ) + * } + * + * // Asynchronous loading + * import { LazyMotion, m } from "framer-motion" + * + * function App() { + * return ( + * import('./path/to/domAnimation')}> + * + * + * ) + * } + * ``` + * + * @public + */ + function LazyMotion({ children, features, strict = false }) { + const [, setIsLoaded] = React$1.useState(!isLazyBundle(features)); + const loadedRenderer = React$1.useRef(undefined); + /** + * If this is a synchronous load, load features immediately + */ + if (!isLazyBundle(features)) { + const { renderer, ...loadedFeatures } = features; + loadedRenderer.current = renderer; + loadFeatures(loadedFeatures); + } + React$1.useEffect(() => { + if (isLazyBundle(features)) { + features().then(({ renderer, ...loadedFeatures }) => { + loadFeatures(loadedFeatures); + loadedRenderer.current = renderer; + setIsLoaded(true); + }); + } + }, []); + return (jsx(LazyContext.Provider, { value: { renderer: loadedRenderer.current, strict }, children: children })); + } + function isLazyBundle(features) { + return typeof features === "function"; + } + + /** + * A list of all valid MotionProps. + * + * @privateRemarks + * This doesn't throw if a `MotionProp` name is missing - it should. + */ + const validMotionProps = new Set([ + "animate", + "exit", + "variants", + "initial", + "style", + "values", + "variants", + "transition", + "transformTemplate", + "custom", + "inherit", + "onBeforeLayoutMeasure", + "onAnimationStart", + "onAnimationComplete", + "onUpdate", + "onDragStart", + "onDrag", + "onDragEnd", + "onMeasureDragConstraints", + "onDirectionLock", + "onDragTransitionEnd", + "_dragX", + "_dragY", + "onHoverStart", + "onHoverEnd", + "onViewportEnter", + "onViewportLeave", + "globalTapTarget", + "ignoreStrict", + "viewport", + ]); + /** + * Check whether a prop name is a valid `MotionProp` key. + * + * @param key - Name of the property to check + * @returns `true` is key is a valid `MotionProp`. + * + * @public + */ + function isValidMotionProp(key) { + return (key.startsWith("while") || + (key.startsWith("drag") && key !== "draggable") || + key.startsWith("layout") || + key.startsWith("onTap") || + key.startsWith("onPan") || + key.startsWith("onLayout") || + validMotionProps.has(key)); + } + + let shouldForward = (key) => !isValidMotionProp(key); + function loadExternalIsValidProp(isValidProp) { + if (typeof isValidProp !== "function") + return; + // Explicitly filter our events + shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key); + } + /** + * Emotion and Styled Components both allow users to pass through arbitrary props to their components + * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which + * of these should be passed to the underlying DOM node. + * + * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props + * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props + * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of + * `@emotion/is-prop-valid`, however to fix this problem we need to use it. + * + * By making it an optionalDependency we can offer this functionality only in the situations where it's + * actually required. + */ + try { + /** + * We attempt to import this package but require won't be defined in esm environments, in that case + * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed + * in favour of explicit injection. + */ + loadExternalIsValidProp(require("@emotion/is-prop-valid").default); + } + catch { + // We don't need to actually do anything here - the fallback is the existing `isPropValid`. + } + function filterProps(props, isDom, forwardMotionProps) { + const filteredProps = {}; + for (const key in props) { + /** + * values is considered a valid prop by Emotion, so if it's present + * this will be rendered out to the DOM unless explicitly filtered. + * + * We check the type as it could be used with the `feColorMatrix` + * element, which we support. + */ + if (key === "values" && typeof props.values === "object") + continue; + if (shouldForward(key) || + (forwardMotionProps === true && isValidMotionProp(key)) || + (!isDom && !isValidMotionProp(key)) || + // If trying to use native HTML drag events, forward drag listeners + (props["draggable"] && + key.startsWith("onDrag"))) { + filteredProps[key] = + props[key]; + } + } + return filteredProps; + } + + /** + * `MotionConfig` is used to set configuration options for all children `motion` components. + * + * ```jsx + * import { motion, MotionConfig } from "framer-motion" + * + * export function App() { + * return ( + * + * + * + * ) + * } + * ``` + * + * @public + */ + function MotionConfig({ children, isValidProp, ...config }) { + isValidProp && loadExternalIsValidProp(isValidProp); + /** + * Inherit props from any parent MotionConfig components + */ + config = { ...React$1.useContext(MotionConfigContext), ...config }; + /** + * Don't allow isStatic to change between renders as it affects how many hooks + * motion components fire. + */ + config.isStatic = useConstant(() => config.isStatic); + /** + * Creating a new config context object will re-render every `motion` component + * every time it renders. So we only want to create a new one sparingly. + */ + const context = React$1.useMemo(() => config, [ + JSON.stringify(config.transition), + config.transformPagePoint, + config.reducedMotion, + ]); + return (jsx(MotionConfigContext.Provider, { value: context, children: children })); + } + + const ReorderContext = React$1.createContext(null); + + function createDOMMotionComponentProxy(componentFactory) { + if (typeof Proxy === "undefined") { + return componentFactory; + } + /** + * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc. + * Rather than generating them anew every render. + */ + const componentCache = new Map(); + const deprecatedFactoryFunction = (...args) => { + { + warnOnce(false, "motion() is deprecated. Use motion.create() instead."); + } + return componentFactory(...args); + }; + return new Proxy(deprecatedFactoryFunction, { + /** + * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc. + * The prop name is passed through as `key` and we can use that to generate a `motion` + * DOM component with that name. + */ + get: (_target, key) => { + if (key === "create") + return componentFactory; + /** + * If this element doesn't exist in the component cache, create it and cache. + */ + if (!componentCache.has(key)) { + componentCache.set(key, componentFactory(key)); + } + return componentCache.get(key); + }, + }); + } + + function resolveVariant(visualElement, definition, custom) { + const props = visualElement.getProps(); + return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement); + } + + const isKeyframesTarget = (v) => { + return Array.isArray(v); + }; + + /** + * Set VisualElement's MotionValue, creating a new MotionValue for it if + * it doesn't exist. + */ + function setMotionValue(visualElement, key, value) { + if (visualElement.hasValue(key)) { + visualElement.getValue(key).set(value); + } + else { + visualElement.addValue(key, motionValue(value)); + } + } + function resolveFinalValueInKeyframes(v) { + // TODO maybe throw if v.length - 1 is placeholder token? + return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; + } + function setTarget(visualElement, definition) { + const resolved = resolveVariant(visualElement, definition); + let { transitionEnd = {}, transition = {}, ...target } = resolved || {}; + target = { ...target, ...transitionEnd }; + for (const key in target) { + const value = resolveFinalValueInKeyframes(target[key]); + setMotionValue(visualElement, key, value); + } + } + + function isWillChangeMotionValue(value) { + return Boolean(isMotionValue(value) && value.add); + } + + function addValueToWillChange(visualElement, key) { + const willChange = visualElement.getValue("willChange"); + /** + * It could be that a user has set willChange to a regular MotionValue, + * in which case we can't add the value to it. + */ + if (isWillChangeMotionValue(willChange)) { + return willChange.add(key); + } + else if (!willChange && MotionGlobalConfig.WillChange) { + const newWillChange = new MotionGlobalConfig.WillChange("auto"); + visualElement.addValue("willChange", newWillChange); + newWillChange.add(key); + } + } + + /** + * Decide whether we should block this animation. Previously, we achieved this + * just by checking whether the key was listed in protectedKeys, but this + * posed problems if an animation was triggered by afterChildren and protectedKeys + * had been set to true in the meantime. + */ + function shouldBlockAnimation({ protectedKeys, needsAnimating }, key) { + const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true; + needsAnimating[key] = false; + return shouldBlock; + } + function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) { + let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition; + if (transitionOverride) + transition = transitionOverride; + const animations = []; + const animationTypeState = type && + visualElement.animationState && + visualElement.animationState.getState()[type]; + for (const key in target) { + const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null); + const valueTarget = target[key]; + if (valueTarget === undefined || + (animationTypeState && + shouldBlockAnimation(animationTypeState, key))) { + continue; + } + const valueTransition = { + delay, + ...getValueTransition$1(transition || {}, key), + }; + /** + * If the value is already at the defined target, skip the animation. + */ + const currentValue = value.get(); + if (currentValue !== undefined && + !value.isAnimating && + !Array.isArray(valueTarget) && + valueTarget === currentValue && + !valueTransition.velocity) { + continue; + } + /** + * If this is the first time a value is being animated, check + * to see if we're handling off from an existing animation. + */ + let isHandoff = false; + if (window.MotionHandoffAnimation) { + const appearId = getOptimisedAppearId(visualElement); + if (appearId) { + const startTime = window.MotionHandoffAnimation(appearId, key, frame); + if (startTime !== null) { + valueTransition.startTime = startTime; + isHandoff = true; + } + } + } + addValueToWillChange(visualElement, key); + value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && positionalKeys.has(key) + ? { type: false } + : valueTransition, visualElement, isHandoff)); + const animation = value.animation; + if (animation) { + animations.push(animation); + } + } + if (transitionEnd) { + Promise.all(animations).then(() => { + frame.update(() => { + transitionEnd && setTarget(visualElement, transitionEnd); + }); + }); + } + return animations; + } + + function animateVariant(visualElement, variant, options = {}) { + const resolved = resolveVariant(visualElement, variant, options.type === "exit" + ? visualElement.presenceContext?.custom + : undefined); + let { transition = visualElement.getDefaultTransition() || {} } = resolved || {}; + if (options.transitionOverride) { + transition = options.transitionOverride; + } + /** + * If we have a variant, create a callback that runs it as an animation. + * Otherwise, we resolve a Promise immediately for a composable no-op. + */ + const getAnimation = resolved + ? () => Promise.all(animateTarget(visualElement, resolved, options)) + : () => Promise.resolve(); + /** + * If we have children, create a callback that runs all their animations. + * Otherwise, we resolve a Promise immediately for a composable no-op. + */ + const getChildAnimations = visualElement.variantChildren && visualElement.variantChildren.size + ? (forwardDelay = 0) => { + const { delayChildren = 0, staggerChildren, staggerDirection, } = transition; + return animateChildren(visualElement, variant, forwardDelay, delayChildren, staggerChildren, staggerDirection, options); + } + : () => Promise.resolve(); + /** + * If the transition explicitly defines a "when" option, we need to resolve either + * this animation or all children animations before playing the other. + */ + const { when } = transition; + if (when) { + const [first, last] = when === "beforeChildren" + ? [getAnimation, getChildAnimations] + : [getChildAnimations, getAnimation]; + return first().then(() => last()); + } + else { + return Promise.all([getAnimation(), getChildAnimations(options.delay)]); + } + } + function animateChildren(visualElement, variant, delay = 0, delayChildren = 0, staggerChildren = 0, staggerDirection = 1, options) { + const animations = []; + const numChildren = visualElement.variantChildren.size; + const maxStaggerDuration = (numChildren - 1) * staggerChildren; + const delayIsFunction = typeof delayChildren === "function"; + const generateStaggerDuration = delayIsFunction + ? (i) => delayChildren(i, numChildren) + : // Support deprecated staggerChildren + staggerDirection === 1 + ? (i = 0) => i * staggerChildren + : (i = 0) => maxStaggerDuration - i * staggerChildren; + Array.from(visualElement.variantChildren) + .sort(sortByTreeOrder) + .forEach((child, i) => { + child.notify("AnimationStart", variant); + animations.push(animateVariant(child, variant, { + ...options, + delay: delay + + (delayIsFunction ? 0 : delayChildren) + + generateStaggerDuration(i), + }).then(() => child.notify("AnimationComplete", variant))); + }); + return Promise.all(animations); + } + function sortByTreeOrder(a, b) { + return a.sortNodePosition(b); + } + + function animateVisualElement(visualElement, definition, options = {}) { + visualElement.notify("AnimationStart", definition); + let animation; + if (Array.isArray(definition)) { + const animations = definition.map((variant) => animateVariant(visualElement, variant, options)); + animation = Promise.all(animations); + } + else if (typeof definition === "string") { + animation = animateVariant(visualElement, definition, options); + } + else { + const resolvedDefinition = typeof definition === "function" + ? resolveVariant(visualElement, definition, options.custom) + : definition; + animation = Promise.all(animateTarget(visualElement, resolvedDefinition, options)); + } + return animation.then(() => { + visualElement.notify("AnimationComplete", definition); + }); + } + + function shallowCompare(next, prev) { + if (!Array.isArray(prev)) + return false; + const prevLength = prev.length; + if (prevLength !== next.length) + return false; + for (let i = 0; i < prevLength; i++) { + if (prev[i] !== next[i]) + return false; + } + return true; + } + + const numVariantProps = variantProps.length; + function getVariantContext(visualElement) { + if (!visualElement) + return undefined; + if (!visualElement.isControllingVariants) { + const context = visualElement.parent + ? getVariantContext(visualElement.parent) || {} + : {}; + if (visualElement.props.initial !== undefined) { + context.initial = visualElement.props.initial; + } + return context; + } + const context = {}; + for (let i = 0; i < numVariantProps; i++) { + const name = variantProps[i]; + const prop = visualElement.props[name]; + if (isVariantLabel(prop) || prop === false) { + context[name] = prop; + } + } + return context; + } + + const reversePriorityOrder = [...variantPriorityOrder].reverse(); + const numAnimationTypes = variantPriorityOrder.length; + function animateList(visualElement) { + return (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options))); + } + function createAnimationState(visualElement) { + let animate = animateList(visualElement); + let state = createState(); + let isInitialRender = true; + /** + * This function will be used to reduce the animation definitions for + * each active animation type into an object of resolved values for it. + */ + const buildResolvedTypeValues = (type) => (acc, definition) => { + const resolved = resolveVariant(visualElement, definition, type === "exit" + ? visualElement.presenceContext?.custom + : undefined); + if (resolved) { + const { transition, transitionEnd, ...target } = resolved; + acc = { ...acc, ...target, ...transitionEnd }; + } + return acc; + }; + /** + * This just allows us to inject mocked animation functions + * @internal + */ + function setAnimateFunction(makeAnimator) { + animate = makeAnimator(visualElement); + } + /** + * When we receive new props, we need to: + * 1. Create a list of protected keys for each type. This is a directory of + * value keys that are currently being "handled" by types of a higher priority + * so that whenever an animation is played of a given type, these values are + * protected from being animated. + * 2. Determine if an animation type needs animating. + * 3. Determine if any values have been removed from a type and figure out + * what to animate those to. + */ + function animateChanges(changedActiveType) { + const { props } = visualElement; + const context = getVariantContext(visualElement.parent) || {}; + /** + * A list of animations that we'll build into as we iterate through the animation + * types. This will get executed at the end of the function. + */ + const animations = []; + /** + * Keep track of which values have been removed. Then, as we hit lower priority + * animation types, we can check if they contain removed values and animate to that. + */ + const removedKeys = new Set(); + /** + * A dictionary of all encountered keys. This is an object to let us build into and + * copy it without iteration. Each time we hit an animation type we set its protected + * keys - the keys its not allowed to animate - to the latest version of this object. + */ + let encounteredKeys = {}; + /** + * If a variant has been removed at a given index, and this component is controlling + * variant animations, we want to ensure lower-priority variants are forced to animate. + */ + let removedVariantIndex = Infinity; + /** + * Iterate through all animation types in reverse priority order. For each, we want to + * detect which values it's handling and whether or not they've changed (and therefore + * need to be animated). If any values have been removed, we want to detect those in + * lower priority props and flag for animation. + */ + for (let i = 0; i < numAnimationTypes; i++) { + const type = reversePriorityOrder[i]; + const typeState = state[type]; + const prop = props[type] !== undefined + ? props[type] + : context[type]; + const propIsVariant = isVariantLabel(prop); + /** + * If this type has *just* changed isActive status, set activeDelta + * to that status. Otherwise set to null. + */ + const activeDelta = type === changedActiveType ? typeState.isActive : null; + if (activeDelta === false) + removedVariantIndex = i; + /** + * If this prop is an inherited variant, rather than been set directly on the + * component itself, we want to make sure we allow the parent to trigger animations. + * + * TODO: Can probably change this to a !isControllingVariants check + */ + let isInherited = prop === context[type] && + prop !== props[type] && + propIsVariant; + /** + * + */ + if (isInherited && + isInitialRender && + visualElement.manuallyAnimateOnMount) { + isInherited = false; + } + /** + * Set all encountered keys so far as the protected keys for this type. This will + * be any key that has been animated or otherwise handled by active, higher-priortiy types. + */ + typeState.protectedKeys = { ...encounteredKeys }; + // Check if we can skip analysing this prop early + if ( + // If it isn't active and hasn't *just* been set as inactive + (!typeState.isActive && activeDelta === null) || + // If we didn't and don't have any defined prop for this animation type + (!prop && !typeState.prevProp) || + // Or if the prop doesn't define an animation + isAnimationControls(prop) || + typeof prop === "boolean") { + continue; + } + /** + * As we go look through the values defined on this type, if we detect + * a changed value or a value that was removed in a higher priority, we set + * this to true and add this prop to the animation list. + */ + const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop); + let shouldAnimateType = variantDidChange || + // If we're making this variant active, we want to always make it active + (type === changedActiveType && + typeState.isActive && + !isInherited && + propIsVariant) || + // If we removed a higher-priority variant (i is in reverse order) + (i > removedVariantIndex && propIsVariant); + let handledRemovedValues = false; + /** + * As animations can be set as variant lists, variants or target objects, we + * coerce everything to an array if it isn't one already + */ + const definitionList = Array.isArray(prop) ? prop : [prop]; + /** + * Build an object of all the resolved values. We'll use this in the subsequent + * animateChanges calls to determine whether a value has changed. + */ + let resolvedValues = definitionList.reduce(buildResolvedTypeValues(type), {}); + if (activeDelta === false) + resolvedValues = {}; + /** + * Now we need to loop through all the keys in the prev prop and this prop, + * and decide: + * 1. If the value has changed, and needs animating + * 2. If it has been removed, and needs adding to the removedKeys set + * 3. If it has been removed in a higher priority type and needs animating + * 4. If it hasn't been removed in a higher priority but hasn't changed, and + * needs adding to the type's protectedKeys list. + */ + const { prevResolvedValues = {} } = typeState; + const allKeys = { + ...prevResolvedValues, + ...resolvedValues, + }; + const markToAnimate = (key) => { + shouldAnimateType = true; + if (removedKeys.has(key)) { + handledRemovedValues = true; + removedKeys.delete(key); + } + typeState.needsAnimating[key] = true; + const motionValue = visualElement.getValue(key); + if (motionValue) + motionValue.liveStyle = false; + }; + for (const key in allKeys) { + const next = resolvedValues[key]; + const prev = prevResolvedValues[key]; + // If we've already handled this we can just skip ahead + if (encounteredKeys.hasOwnProperty(key)) + continue; + /** + * If the value has changed, we probably want to animate it. + */ + let valueHasChanged = false; + if (isKeyframesTarget(next) && isKeyframesTarget(prev)) { + valueHasChanged = !shallowCompare(next, prev); + } + else { + valueHasChanged = next !== prev; + } + if (valueHasChanged) { + if (next !== undefined && next !== null) { + // If next is defined and doesn't equal prev, it needs animating + markToAnimate(key); + } + else { + // If it's undefined, it's been removed. + removedKeys.add(key); + } + } + else if (next !== undefined && removedKeys.has(key)) { + /** + * If next hasn't changed and it isn't undefined, we want to check if it's + * been removed by a higher priority + */ + markToAnimate(key); + } + else { + /** + * If it hasn't changed, we add it to the list of protected values + * to ensure it doesn't get animated. + */ + typeState.protectedKeys[key] = true; + } + } + /** + * Update the typeState so next time animateChanges is called we can compare the + * latest prop and resolvedValues to these. + */ + typeState.prevProp = prop; + typeState.prevResolvedValues = resolvedValues; + /** + * + */ + if (typeState.isActive) { + encounteredKeys = { ...encounteredKeys, ...resolvedValues }; + } + if (isInitialRender && visualElement.blockInitialAnimation) { + shouldAnimateType = false; + } + /** + * If this is an inherited prop we want to skip this animation + * unless the inherited variants haven't changed on this render. + */ + const willAnimateViaParent = isInherited && variantDidChange; + const needsAnimating = !willAnimateViaParent || handledRemovedValues; + if (shouldAnimateType && needsAnimating) { + animations.push(...definitionList.map((animation) => ({ + animation: animation, + options: { type }, + }))); + } + } + /** + * If there are some removed value that haven't been dealt with, + * we need to create a new animation that falls back either to the value + * defined in the style prop, or the last read value. + */ + if (removedKeys.size) { + const fallbackAnimation = {}; + /** + * If the initial prop contains a transition we can use that, otherwise + * allow the animation function to use the visual element's default. + */ + if (typeof props.initial !== "boolean") { + const initialTransition = resolveVariant(visualElement, Array.isArray(props.initial) + ? props.initial[0] + : props.initial); + if (initialTransition && initialTransition.transition) { + fallbackAnimation.transition = initialTransition.transition; + } + } + removedKeys.forEach((key) => { + const fallbackTarget = visualElement.getBaseTarget(key); + const motionValue = visualElement.getValue(key); + if (motionValue) + motionValue.liveStyle = true; + // @ts-expect-error - @mattgperry to figure if we should do something here + fallbackAnimation[key] = fallbackTarget ?? null; + }); + animations.push({ animation: fallbackAnimation }); + } + let shouldAnimate = Boolean(animations.length); + if (isInitialRender && + (props.initial === false || props.initial === props.animate) && + !visualElement.manuallyAnimateOnMount) { + shouldAnimate = false; + } + isInitialRender = false; + return shouldAnimate ? animate(animations) : Promise.resolve(); + } + /** + * Change whether a certain animation type is active. + */ + function setActive(type, isActive) { + // If the active state hasn't changed, we can safely do nothing here + if (state[type].isActive === isActive) + return Promise.resolve(); + // Propagate active change to children + visualElement.variantChildren?.forEach((child) => child.animationState?.setActive(type, isActive)); + state[type].isActive = isActive; + const animations = animateChanges(type); + for (const key in state) { + state[key].protectedKeys = {}; + } + return animations; + } + return { + animateChanges, + setActive, + setAnimateFunction, + getState: () => state, + reset: () => { + state = createState(); + isInitialRender = true; + }, + }; + } + function checkVariantsDidChange(prev, next) { + if (typeof next === "string") { + return next !== prev; + } + else if (Array.isArray(next)) { + return !shallowCompare(next, prev); + } + return false; + } + function createTypeState(isActive = false) { + return { + isActive, + protectedKeys: {}, + needsAnimating: {}, + prevResolvedValues: {}, + }; + } + function createState() { + return { + animate: createTypeState(true), + whileInView: createTypeState(), + whileHover: createTypeState(), + whileTap: createTypeState(), + whileDrag: createTypeState(), + whileFocus: createTypeState(), + exit: createTypeState(), + }; + } + + class Feature { + constructor(node) { + this.isMounted = false; + this.node = node; + } + update() { } + } + + class AnimationFeature extends Feature { + /** + * We dynamically generate the AnimationState manager as it contains a reference + * to the underlying animation library. We only want to load that if we load this, + * so people can optionally code split it out using the `m` component. + */ + constructor(node) { + super(node); + node.animationState || (node.animationState = createAnimationState(node)); + } + updateAnimationControlsSubscription() { + const { animate } = this.node.getProps(); + if (isAnimationControls(animate)) { + this.unmountControls = animate.subscribe(this.node); + } + } + /** + * Subscribe any provided AnimationControls to the component's VisualElement + */ + mount() { + this.updateAnimationControlsSubscription(); + } + update() { + const { animate } = this.node.getProps(); + const { animate: prevAnimate } = this.node.prevProps || {}; + if (animate !== prevAnimate) { + this.updateAnimationControlsSubscription(); + } + } + unmount() { + this.node.animationState.reset(); + this.unmountControls?.(); + } + } + + let id$1 = 0; + class ExitAnimationFeature extends Feature { + constructor() { + super(...arguments); + this.id = id$1++; + } + update() { + if (!this.node.presenceContext) + return; + const { isPresent, onExitComplete } = this.node.presenceContext; + const { isPresent: prevIsPresent } = this.node.prevPresenceContext || {}; + if (!this.node.animationState || isPresent === prevIsPresent) { + return; + } + const exitAnimation = this.node.animationState.setActive("exit", !isPresent); + if (onExitComplete && !isPresent) { + exitAnimation.then(() => { + onExitComplete(this.id); + }); + } + } + mount() { + const { register, onExitComplete } = this.node.presenceContext || {}; + if (onExitComplete) { + onExitComplete(this.id); + } + if (register) { + this.unmount = register(this.id); + } + } + unmount() { } + } + + const animations = { + animation: { + Feature: AnimationFeature, + }, + exit: { + Feature: ExitAnimationFeature, + }, + }; + + function extractEventInfo(event) { + return { + point: { + x: event.pageX, + y: event.pageY, + }, + }; + } + const addPointerInfo = (handler) => { + return (event) => isPrimaryPointer(event) && handler(event, extractEventInfo(event)); + }; + + function addPointerEvent(target, eventName, handler, options) { + return addDomEvent(target, eventName, addPointerInfo(handler), options); + } + + // Fixes https://github.com/motiondivision/motion/issues/2270 + const getContextWindow = ({ current }) => { + return current ? current.ownerDocument.defaultView : null; + }; + + function isRefObject(ref) { + return (ref && + typeof ref === "object" && + Object.prototype.hasOwnProperty.call(ref, "current")); + } + + const distance = (a, b) => Math.abs(a - b); + function distance2D(a, b) { + // Multi-dimensional + const xDelta = distance(a.x, b.x); + const yDelta = distance(a.y, b.y); + return Math.sqrt(xDelta ** 2 + yDelta ** 2); + } + + /** + * @internal + */ + class PanSession { + constructor(event, handlers, { transformPagePoint, contextWindow = window, dragSnapToOrigin = false, distanceThreshold = 3, } = {}) { + /** + * @internal + */ + this.startEvent = null; + /** + * @internal + */ + this.lastMoveEvent = null; + /** + * @internal + */ + this.lastMoveEventInfo = null; + /** + * @internal + */ + this.handlers = {}; + /** + * @internal + */ + this.contextWindow = window; + this.updatePoint = () => { + if (!(this.lastMoveEvent && this.lastMoveEventInfo)) + return; + const info = getPanInfo(this.lastMoveEventInfo, this.history); + const isPanStarted = this.startEvent !== null; + // Only start panning if the offset is larger than 3 pixels. If we make it + // any larger than this we'll want to reset the pointer history + // on the first update to avoid visual snapping to the cursor. + const isDistancePastThreshold = distance2D(info.offset, { x: 0, y: 0 }) >= this.distanceThreshold; + if (!isPanStarted && !isDistancePastThreshold) + return; + const { point } = info; + const { timestamp } = frameData; + this.history.push({ ...point, timestamp }); + const { onStart, onMove } = this.handlers; + if (!isPanStarted) { + onStart && onStart(this.lastMoveEvent, info); + this.startEvent = this.lastMoveEvent; + } + onMove && onMove(this.lastMoveEvent, info); + }; + this.handlePointerMove = (event, info) => { + this.lastMoveEvent = event; + this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint); + // Throttle mouse move event to once per frame + frame.update(this.updatePoint, true); + }; + this.handlePointerUp = (event, info) => { + this.end(); + const { onEnd, onSessionEnd, resumeAnimation } = this.handlers; + if (this.dragSnapToOrigin) + resumeAnimation && resumeAnimation(); + if (!(this.lastMoveEvent && this.lastMoveEventInfo)) + return; + const panInfo = getPanInfo(event.type === "pointercancel" + ? this.lastMoveEventInfo + : transformPoint(info, this.transformPagePoint), this.history); + if (this.startEvent && onEnd) { + onEnd(event, panInfo); + } + onSessionEnd && onSessionEnd(event, panInfo); + }; + // If we have more than one touch, don't start detecting this gesture + if (!isPrimaryPointer(event)) + return; + this.dragSnapToOrigin = dragSnapToOrigin; + this.handlers = handlers; + this.transformPagePoint = transformPagePoint; + this.distanceThreshold = distanceThreshold; + this.contextWindow = contextWindow || window; + const info = extractEventInfo(event); + const initialInfo = transformPoint(info, this.transformPagePoint); + const { point } = initialInfo; + const { timestamp } = frameData; + this.history = [{ ...point, timestamp }]; + const { onSessionStart } = handlers; + onSessionStart && + onSessionStart(event, getPanInfo(initialInfo, this.history)); + this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp)); + } + updateHandlers(handlers) { + this.handlers = handlers; + } + end() { + this.removeListeners && this.removeListeners(); + cancelFrame(this.updatePoint); + } + } + function transformPoint(info, transformPagePoint) { + return transformPagePoint ? { point: transformPagePoint(info.point) } : info; + } + function subtractPoint(a, b) { + return { x: a.x - b.x, y: a.y - b.y }; + } + function getPanInfo({ point }, history) { + return { + point, + delta: subtractPoint(point, lastDevicePoint(history)), + offset: subtractPoint(point, startDevicePoint(history)), + velocity: getVelocity(history, 0.1), + }; + } + function startDevicePoint(history) { + return history[0]; + } + function lastDevicePoint(history) { + return history[history.length - 1]; + } + function getVelocity(history, timeDelta) { + if (history.length < 2) { + return { x: 0, y: 0 }; + } + let i = history.length - 1; + let timestampedPoint = null; + const lastPoint = lastDevicePoint(history); + while (i >= 0) { + timestampedPoint = history[i]; + if (lastPoint.timestamp - timestampedPoint.timestamp > + secondsToMilliseconds(timeDelta)) { + break; + } + i--; + } + if (!timestampedPoint) { + return { x: 0, y: 0 }; + } + const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp); + if (time === 0) { + return { x: 0, y: 0 }; + } + const currentVelocity = { + x: (lastPoint.x - timestampedPoint.x) / time, + y: (lastPoint.y - timestampedPoint.y) / time, + }; + if (currentVelocity.x === Infinity) { + currentVelocity.x = 0; + } + if (currentVelocity.y === Infinity) { + currentVelocity.y = 0; + } + return currentVelocity; + } + + /** + * Apply constraints to a point. These constraints are both physical along an + * axis, and an elastic factor that determines how much to constrain the point + * by if it does lie outside the defined parameters. + */ + function applyConstraints(point, { min, max }, elastic) { + if (min !== undefined && point < min) { + // If we have a min point defined, and this is outside of that, constrain + point = elastic + ? mixNumber$1(min, point, elastic.min) + : Math.max(point, min); + } + else if (max !== undefined && point > max) { + // If we have a max point defined, and this is outside of that, constrain + point = elastic + ? mixNumber$1(max, point, elastic.max) + : Math.min(point, max); + } + return point; + } + /** + * Calculate constraints in terms of the viewport when defined relatively to the + * measured axis. This is measured from the nearest edge, so a max constraint of 200 + * on an axis with a max value of 300 would return a constraint of 500 - axis length + */ + function calcRelativeAxisConstraints(axis, min, max) { + return { + min: min !== undefined ? axis.min + min : undefined, + max: max !== undefined + ? axis.max + max - (axis.max - axis.min) + : undefined, + }; + } + /** + * Calculate constraints in terms of the viewport when + * defined relatively to the measured bounding box. + */ + function calcRelativeConstraints(layoutBox, { top, left, bottom, right }) { + return { + x: calcRelativeAxisConstraints(layoutBox.x, left, right), + y: calcRelativeAxisConstraints(layoutBox.y, top, bottom), + }; + } + /** + * Calculate viewport constraints when defined as another viewport-relative axis + */ + function calcViewportAxisConstraints(layoutAxis, constraintsAxis) { + let min = constraintsAxis.min - layoutAxis.min; + let max = constraintsAxis.max - layoutAxis.max; + // If the constraints axis is actually smaller than the layout axis then we can + // flip the constraints + if (constraintsAxis.max - constraintsAxis.min < + layoutAxis.max - layoutAxis.min) { + [min, max] = [max, min]; + } + return { min, max }; + } + /** + * Calculate viewport constraints when defined as another viewport-relative box + */ + function calcViewportConstraints(layoutBox, constraintsBox) { + return { + x: calcViewportAxisConstraints(layoutBox.x, constraintsBox.x), + y: calcViewportAxisConstraints(layoutBox.y, constraintsBox.y), + }; + } + /** + * Calculate a transform origin relative to the source axis, between 0-1, that results + * in an asthetically pleasing scale/transform needed to project from source to target. + */ + function calcOrigin(source, target) { + let origin = 0.5; + const sourceLength = calcLength(source); + const targetLength = calcLength(target); + if (targetLength > sourceLength) { + origin = progress(target.min, target.max - sourceLength, source.min); + } + else if (sourceLength > targetLength) { + origin = progress(source.min, source.max - targetLength, target.min); + } + return clamp(0, 1, origin); + } + /** + * Rebase the calculated viewport constraints relative to the layout.min point. + */ + function rebaseAxisConstraints(layout, constraints) { + const relativeConstraints = {}; + if (constraints.min !== undefined) { + relativeConstraints.min = constraints.min - layout.min; + } + if (constraints.max !== undefined) { + relativeConstraints.max = constraints.max - layout.min; + } + return relativeConstraints; + } + const defaultElastic = 0.35; + /** + * Accepts a dragElastic prop and returns resolved elastic values for each axis. + */ + function resolveDragElastic(dragElastic = defaultElastic) { + if (dragElastic === false) { + dragElastic = 0; + } + else if (dragElastic === true) { + dragElastic = defaultElastic; + } + return { + x: resolveAxisElastic(dragElastic, "left", "right"), + y: resolveAxisElastic(dragElastic, "top", "bottom"), + }; + } + function resolveAxisElastic(dragElastic, minLabel, maxLabel) { + return { + min: resolvePointElastic(dragElastic, minLabel), + max: resolvePointElastic(dragElastic, maxLabel), + }; + } + function resolvePointElastic(dragElastic, label) { + return typeof dragElastic === "number" + ? dragElastic + : dragElastic[label] || 0; + } + + const elementDragControls = new WeakMap(); + class VisualElementDragControls { + constructor(visualElement) { + this.openDragLock = null; + this.isDragging = false; + this.currentDirection = null; + this.originPoint = { x: 0, y: 0 }; + /** + * The permitted boundaries of travel, in pixels. + */ + this.constraints = false; + this.hasMutatedConstraints = false; + /** + * The per-axis resolved elastic values. + */ + this.elastic = createBox(); + /** + * The latest pointer event. Used as fallback when the `cancel` and `stop` functions are called without arguments. + */ + this.latestPointerEvent = null; + /** + * The latest pan info. Used as fallback when the `cancel` and `stop` functions are called without arguments. + */ + this.latestPanInfo = null; + this.visualElement = visualElement; + } + start(originEvent, { snapToCursor = false, distanceThreshold } = {}) { + /** + * Don't start dragging if this component is exiting + */ + const { presenceContext } = this.visualElement; + if (presenceContext && presenceContext.isPresent === false) + return; + const onSessionStart = (event) => { + const { dragSnapToOrigin } = this.getProps(); + // Stop or pause any animations on both axis values immediately. This allows the user to throw and catch + // the component. + dragSnapToOrigin ? this.pauseAnimation() : this.stopAnimation(); + if (snapToCursor) { + this.snapToCursor(extractEventInfo(event).point); + } + }; + const onStart = (event, info) => { + // Attempt to grab the global drag gesture lock - maybe make this part of PanSession + const { drag, dragPropagation, onDragStart } = this.getProps(); + if (drag && !dragPropagation) { + if (this.openDragLock) + this.openDragLock(); + this.openDragLock = setDragLock(drag); + // If we don 't have the lock, don't start dragging + if (!this.openDragLock) + return; + } + this.latestPointerEvent = event; + this.latestPanInfo = info; + this.isDragging = true; + this.currentDirection = null; + this.resolveConstraints(); + if (this.visualElement.projection) { + this.visualElement.projection.isAnimationBlocked = true; + this.visualElement.projection.target = undefined; + } + /** + * Record gesture origin + */ + eachAxis((axis) => { + let current = this.getAxisMotionValue(axis).get() || 0; + /** + * If the MotionValue is a percentage value convert to px + */ + if (percent.test(current)) { + const { projection } = this.visualElement; + if (projection && projection.layout) { + const measuredAxis = projection.layout.layoutBox[axis]; + if (measuredAxis) { + const length = calcLength(measuredAxis); + current = length * (parseFloat(current) / 100); + } + } + } + this.originPoint[axis] = current; + }); + // Fire onDragStart event + if (onDragStart) { + frame.postRender(() => onDragStart(event, info)); + } + addValueToWillChange(this.visualElement, "transform"); + const { animationState } = this.visualElement; + animationState && animationState.setActive("whileDrag", true); + }; + const onMove = (event, info) => { + this.latestPointerEvent = event; + this.latestPanInfo = info; + const { dragPropagation, dragDirectionLock, onDirectionLock, onDrag, } = this.getProps(); + // If we didn't successfully receive the gesture lock, early return. + if (!dragPropagation && !this.openDragLock) + return; + const { offset } = info; + // Attempt to detect drag direction if directionLock is true + if (dragDirectionLock && this.currentDirection === null) { + this.currentDirection = getCurrentDirection(offset); + // If we've successfully set a direction, notify listener + if (this.currentDirection !== null) { + onDirectionLock && onDirectionLock(this.currentDirection); + } + return; + } + // Update each point with the latest position + this.updateAxis("x", info.point, offset); + this.updateAxis("y", info.point, offset); + /** + * Ideally we would leave the renderer to fire naturally at the end of + * this frame but if the element is about to change layout as the result + * of a re-render we want to ensure the browser can read the latest + * bounding box to ensure the pointer and element don't fall out of sync. + */ + this.visualElement.render(); + /** + * This must fire after the render call as it might trigger a state + * change which itself might trigger a layout update. + */ + onDrag && onDrag(event, info); + }; + const onSessionEnd = (event, info) => { + this.latestPointerEvent = event; + this.latestPanInfo = info; + this.stop(event, info); + this.latestPointerEvent = null; + this.latestPanInfo = null; + }; + const resumeAnimation = () => eachAxis((axis) => this.getAnimationState(axis) === "paused" && + this.getAxisMotionValue(axis).animation?.play()); + const { dragSnapToOrigin } = this.getProps(); + this.panSession = new PanSession(originEvent, { + onSessionStart, + onStart, + onMove, + onSessionEnd, + resumeAnimation, + }, { + transformPagePoint: this.visualElement.getTransformPagePoint(), + dragSnapToOrigin, + distanceThreshold, + contextWindow: getContextWindow(this.visualElement), + }); + } + /** + * @internal + */ + stop(event, panInfo) { + const finalEvent = event || this.latestPointerEvent; + const finalPanInfo = panInfo || this.latestPanInfo; + const isDragging = this.isDragging; + this.cancel(); + if (!isDragging || !finalPanInfo || !finalEvent) + return; + const { velocity } = finalPanInfo; + this.startAnimation(velocity); + const { onDragEnd } = this.getProps(); + if (onDragEnd) { + frame.postRender(() => onDragEnd(finalEvent, finalPanInfo)); + } + } + /** + * @internal + */ + cancel() { + this.isDragging = false; + const { projection, animationState } = this.visualElement; + if (projection) { + projection.isAnimationBlocked = false; + } + this.panSession && this.panSession.end(); + this.panSession = undefined; + const { dragPropagation } = this.getProps(); + if (!dragPropagation && this.openDragLock) { + this.openDragLock(); + this.openDragLock = null; + } + animationState && animationState.setActive("whileDrag", false); + } + updateAxis(axis, _point, offset) { + const { drag } = this.getProps(); + // If we're not dragging this axis, do an early return. + if (!offset || !shouldDrag(axis, drag, this.currentDirection)) + return; + const axisValue = this.getAxisMotionValue(axis); + let next = this.originPoint[axis] + offset[axis]; + // Apply constraints + if (this.constraints && this.constraints[axis]) { + next = applyConstraints(next, this.constraints[axis], this.elastic[axis]); + } + axisValue.set(next); + } + resolveConstraints() { + const { dragConstraints, dragElastic } = this.getProps(); + const layout = this.visualElement.projection && + !this.visualElement.projection.layout + ? this.visualElement.projection.measure(false) + : this.visualElement.projection?.layout; + const prevConstraints = this.constraints; + if (dragConstraints && isRefObject(dragConstraints)) { + if (!this.constraints) { + this.constraints = this.resolveRefConstraints(); + } + } + else { + if (dragConstraints && layout) { + this.constraints = calcRelativeConstraints(layout.layoutBox, dragConstraints); + } + else { + this.constraints = false; + } + } + this.elastic = resolveDragElastic(dragElastic); + /** + * If we're outputting to external MotionValues, we want to rebase the measured constraints + * from viewport-relative to component-relative. + */ + if (prevConstraints !== this.constraints && + layout && + this.constraints && + !this.hasMutatedConstraints) { + eachAxis((axis) => { + if (this.constraints !== false && + this.getAxisMotionValue(axis)) { + this.constraints[axis] = rebaseAxisConstraints(layout.layoutBox[axis], this.constraints[axis]); + } + }); + } + } + resolveRefConstraints() { + const { dragConstraints: constraints, onMeasureDragConstraints } = this.getProps(); + if (!constraints || !isRefObject(constraints)) + return false; + const constraintsElement = constraints.current; + exports.invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop."); + const { projection } = this.visualElement; + // TODO + if (!projection || !projection.layout) + return false; + const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint()); + let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox); + /** + * If there's an onMeasureDragConstraints listener we call it and + * if different constraints are returned, set constraints to that + */ + if (onMeasureDragConstraints) { + const userConstraints = onMeasureDragConstraints(convertBoxToBoundingBox(measuredConstraints)); + this.hasMutatedConstraints = !!userConstraints; + if (userConstraints) { + measuredConstraints = convertBoundingBoxToBox(userConstraints); + } + } + return measuredConstraints; + } + startAnimation(velocity) { + const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps(); + const constraints = this.constraints || {}; + const momentumAnimations = eachAxis((axis) => { + if (!shouldDrag(axis, drag, this.currentDirection)) { + return; + } + let transition = (constraints && constraints[axis]) || {}; + if (dragSnapToOrigin) + transition = { min: 0, max: 0 }; + /** + * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame + * of spring animations so we should look into adding a disable spring option to `inertia`. + * We could do something here where we affect the `bounceStiffness` and `bounceDamping` + * using the value of `dragElastic`. + */ + const bounceStiffness = dragElastic ? 200 : 1000000; + const bounceDamping = dragElastic ? 40 : 10000000; + const inertia = { + type: "inertia", + velocity: dragMomentum ? velocity[axis] : 0, + bounceStiffness, + bounceDamping, + timeConstant: 750, + restDelta: 1, + restSpeed: 10, + ...dragTransition, + ...transition, + }; + // If we're not animating on an externally-provided `MotionValue` we can use the + // component's animation controls which will handle interactions with whileHover (etc), + // otherwise we just have to animate the `MotionValue` itself. + return this.startAxisValueAnimation(axis, inertia); + }); + // Run all animations and then resolve the new drag constraints. + return Promise.all(momentumAnimations).then(onDragTransitionEnd); + } + startAxisValueAnimation(axis, transition) { + const axisValue = this.getAxisMotionValue(axis); + addValueToWillChange(this.visualElement, axis); + return axisValue.start(animateMotionValue(axis, axisValue, 0, transition, this.visualElement, false)); + } + stopAnimation() { + eachAxis((axis) => this.getAxisMotionValue(axis).stop()); + } + pauseAnimation() { + eachAxis((axis) => this.getAxisMotionValue(axis).animation?.pause()); + } + getAnimationState(axis) { + return this.getAxisMotionValue(axis).animation?.state; + } + /** + * Drag works differently depending on which props are provided. + * + * - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values. + * - Otherwise, we apply the delta to the x/y motion values. + */ + getAxisMotionValue(axis) { + const dragKey = `_drag${axis.toUpperCase()}`; + const props = this.visualElement.getProps(); + const externalMotionValue = props[dragKey]; + return externalMotionValue + ? externalMotionValue + : this.visualElement.getValue(axis, (props.initial + ? props.initial[axis] + : undefined) || 0); + } + snapToCursor(point) { + eachAxis((axis) => { + const { drag } = this.getProps(); + // If we're not dragging this axis, do an early return. + if (!shouldDrag(axis, drag, this.currentDirection)) + return; + const { projection } = this.visualElement; + const axisValue = this.getAxisMotionValue(axis); + if (projection && projection.layout) { + const { min, max } = projection.layout.layoutBox[axis]; + axisValue.set(point[axis] - mixNumber$1(min, max, 0.5)); + } + }); + } + /** + * When the viewport resizes we want to check if the measured constraints + * have changed and, if so, reposition the element within those new constraints + * relative to where it was before the resize. + */ + scalePositionWithinConstraints() { + if (!this.visualElement.current) + return; + const { drag, dragConstraints } = this.getProps(); + const { projection } = this.visualElement; + if (!isRefObject(dragConstraints) || !projection || !this.constraints) + return; + /** + * Stop current animations as there can be visual glitching if we try to do + * this mid-animation + */ + this.stopAnimation(); + /** + * Record the relative position of the dragged element relative to the + * constraints box and save as a progress value. + */ + const boxProgress = { x: 0, y: 0 }; + eachAxis((axis) => { + const axisValue = this.getAxisMotionValue(axis); + if (axisValue && this.constraints !== false) { + const latest = axisValue.get(); + boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]); + } + }); + /** + * Update the layout of this element and resolve the latest drag constraints + */ + const { transformTemplate } = this.visualElement.getProps(); + this.visualElement.current.style.transform = transformTemplate + ? transformTemplate({}, "") + : "none"; + projection.root && projection.root.updateScroll(); + projection.updateLayout(); + this.resolveConstraints(); + /** + * For each axis, calculate the current progress of the layout axis + * within the new constraints. + */ + eachAxis((axis) => { + if (!shouldDrag(axis, drag, null)) + return; + /** + * Calculate a new transform based on the previous box progress + */ + const axisValue = this.getAxisMotionValue(axis); + const { min, max } = this.constraints[axis]; + axisValue.set(mixNumber$1(min, max, boxProgress[axis])); + }); + } + addListeners() { + if (!this.visualElement.current) + return; + elementDragControls.set(this.visualElement, this); + const element = this.visualElement.current; + /** + * Attach a pointerdown event listener on this DOM element to initiate drag tracking. + */ + const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => { + const { drag, dragListener = true } = this.getProps(); + drag && dragListener && this.start(event); + }); + const measureDragConstraints = () => { + const { dragConstraints } = this.getProps(); + if (isRefObject(dragConstraints) && dragConstraints.current) { + this.constraints = this.resolveRefConstraints(); + } + }; + const { projection } = this.visualElement; + const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints); + if (projection && !projection.layout) { + projection.root && projection.root.updateScroll(); + projection.updateLayout(); + } + frame.read(measureDragConstraints); + /** + * Attach a window resize listener to scale the draggable target within its defined + * constraints as the window resizes. + */ + const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints()); + /** + * If the element's layout changes, calculate the delta and apply that to + * the drag gesture's origin point. + */ + const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => { + if (this.isDragging && hasLayoutChanged) { + eachAxis((axis) => { + const motionValue = this.getAxisMotionValue(axis); + if (!motionValue) + return; + this.originPoint[axis] += delta[axis].translate; + motionValue.set(motionValue.get() + delta[axis].translate); + }); + this.visualElement.render(); + } + })); + return () => { + stopResizeListener(); + stopPointerListener(); + stopMeasureLayoutListener(); + stopLayoutUpdateListener && stopLayoutUpdateListener(); + }; + } + getProps() { + const props = this.visualElement.getProps(); + const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props; + return { + ...props, + drag, + dragDirectionLock, + dragPropagation, + dragConstraints, + dragElastic, + dragMomentum, + }; + } + } + function shouldDrag(direction, drag, currentDirection) { + return ((drag === true || drag === direction) && + (currentDirection === null || currentDirection === direction)); + } + /** + * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower + * than the provided threshold, return `null`. + * + * @param offset - The x/y offset from origin. + * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction. + */ + function getCurrentDirection(offset, lockThreshold = 10) { + let direction = null; + if (Math.abs(offset.y) > lockThreshold) { + direction = "y"; + } + else if (Math.abs(offset.x) > lockThreshold) { + direction = "x"; + } + return direction; + } + + class DragGesture extends Feature { + constructor(node) { + super(node); + this.removeGroupControls = noop; + this.removeListeners = noop; + this.controls = new VisualElementDragControls(node); + } + mount() { + // If we've been provided a DragControls for manual control over the drag gesture, + // subscribe this component to it on mount. + const { dragControls } = this.node.getProps(); + if (dragControls) { + this.removeGroupControls = dragControls.subscribe(this.controls); + } + this.removeListeners = this.controls.addListeners() || noop; + } + unmount() { + this.removeGroupControls(); + this.removeListeners(); + } + } + + const asyncHandler = (handler) => (event, info) => { + if (handler) { + frame.postRender(() => handler(event, info)); + } + }; + class PanGesture extends Feature { + constructor() { + super(...arguments); + this.removePointerDownListener = noop; + } + onPointerDown(pointerDownEvent) { + this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), { + transformPagePoint: this.node.getTransformPagePoint(), + contextWindow: getContextWindow(this.node), + }); + } + createPanHandlers() { + const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps(); + return { + onSessionStart: asyncHandler(onPanSessionStart), + onStart: asyncHandler(onPanStart), + onMove: onPan, + onEnd: (event, info) => { + delete this.session; + if (onPanEnd) { + frame.postRender(() => onPanEnd(event, info)); + } + }, + }; + } + mount() { + this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event)); + } + update() { + this.session && this.session.updateHandlers(this.createPanHandlers()); + } + unmount() { + this.removePointerDownListener(); + this.session && this.session.end(); + } + } + + /** + * Internal, exported only for usage in Framer + */ + const SwitchLayoutGroupContext = React$1.createContext({}); + + /** + * Track whether we've taken any snapshots yet. If not, + * we can safely skip notification of didUpdate. + */ + let hasTakenAnySnapshot = false; + class MeasureLayoutWithContext extends React$1.Component { + /** + * This only mounts projection nodes for components that + * need measuring, we might want to do it for all components + * in order to incorporate transforms + */ + componentDidMount() { + const { visualElement, layoutGroup, switchLayoutGroup, layoutId } = this.props; + const { projection } = visualElement; + addScaleCorrector(defaultScaleCorrectors); + if (projection) { + if (layoutGroup.group) + layoutGroup.group.add(projection); + if (switchLayoutGroup && switchLayoutGroup.register && layoutId) { + switchLayoutGroup.register(projection); + } + if (hasTakenAnySnapshot) { + projection.root.didUpdate(); + } + projection.addEventListener("animationComplete", () => { + this.safeToRemove(); + }); + projection.setOptions({ + ...projection.options, + onExitComplete: () => this.safeToRemove(), + }); + } + globalProjectionState.hasEverUpdated = true; + } + getSnapshotBeforeUpdate(prevProps) { + const { layoutDependency, visualElement, drag, isPresent } = this.props; + const { projection } = visualElement; + if (!projection) + return null; + /** + * TODO: We use this data in relegate to determine whether to + * promote a previous element. There's no guarantee its presence data + * will have updated by this point - if a bug like this arises it will + * have to be that we markForRelegation and then find a new lead some other way, + * perhaps in didUpdate + */ + projection.isPresent = isPresent; + hasTakenAnySnapshot = true; + if (drag || + prevProps.layoutDependency !== layoutDependency || + layoutDependency === undefined || + prevProps.isPresent !== isPresent) { + projection.willUpdate(); + } + else { + this.safeToRemove(); + } + if (prevProps.isPresent !== isPresent) { + if (isPresent) { + projection.promote(); + } + else if (!projection.relegate()) { + /** + * If there's another stack member taking over from this one, + * it's in charge of the exit animation and therefore should + * be in charge of the safe to remove. Otherwise we call it here. + */ + frame.postRender(() => { + const stack = projection.getStack(); + if (!stack || !stack.members.length) { + this.safeToRemove(); + } + }); + } + } + return null; + } + componentDidUpdate() { + const { projection } = this.props.visualElement; + if (projection) { + projection.root.didUpdate(); + microtask.postRender(() => { + if (!projection.currentAnimation && projection.isLead()) { + this.safeToRemove(); + } + }); + } + } + componentWillUnmount() { + const { visualElement, layoutGroup, switchLayoutGroup: promoteContext, } = this.props; + const { projection } = visualElement; + if (projection) { + projection.scheduleCheckAfterUnmount(); + if (layoutGroup && layoutGroup.group) + layoutGroup.group.remove(projection); + if (promoteContext && promoteContext.deregister) + promoteContext.deregister(projection); + } + } + safeToRemove() { + const { safeToRemove } = this.props; + safeToRemove && safeToRemove(); + } + render() { + return null; + } + } + function MeasureLayout(props) { + const [isPresent, safeToRemove] = usePresence(); + const layoutGroup = React$1.useContext(LayoutGroupContext); + return (jsx(MeasureLayoutWithContext, { ...props, layoutGroup: layoutGroup, switchLayoutGroup: React$1.useContext(SwitchLayoutGroupContext), isPresent: isPresent, safeToRemove: safeToRemove })); + } + const defaultScaleCorrectors = { + borderRadius: { + ...correctBorderRadius, + applyTo: [ + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomLeftRadius", + "borderBottomRightRadius", + ], + }, + borderTopLeftRadius: correctBorderRadius, + borderTopRightRadius: correctBorderRadius, + borderBottomLeftRadius: correctBorderRadius, + borderBottomRightRadius: correctBorderRadius, + boxShadow: correctBoxShadow, + }; + + const drag = { + pan: { + Feature: PanGesture, + }, + drag: { + Feature: DragGesture, + ProjectionNode: HTMLProjectionNode, + MeasureLayout, + }, + }; + + function handleHoverEvent(node, event, lifecycle) { + const { props } = node; + if (node.animationState && props.whileHover) { + node.animationState.setActive("whileHover", lifecycle === "Start"); + } + const eventName = ("onHover" + lifecycle); + const callback = props[eventName]; + if (callback) { + frame.postRender(() => callback(event, extractEventInfo(event))); + } + } + class HoverGesture extends Feature { + mount() { + const { current } = this.node; + if (!current) + return; + this.unmount = hover(current, (_element, startEvent) => { + handleHoverEvent(this.node, startEvent, "Start"); + return (endEvent) => handleHoverEvent(this.node, endEvent, "End"); + }); + } + unmount() { } + } + + class FocusGesture extends Feature { + constructor() { + super(...arguments); + this.isActive = false; + } + onFocus() { + let isFocusVisible = false; + /** + * If this element doesn't match focus-visible then don't + * apply whileHover. But, if matches throws that focus-visible + * is not a valid selector then in that browser outline styles will be applied + * to the element by default and we want to match that behaviour with whileFocus. + */ + try { + isFocusVisible = this.node.current.matches(":focus-visible"); + } + catch (e) { + isFocusVisible = true; + } + if (!isFocusVisible || !this.node.animationState) + return; + this.node.animationState.setActive("whileFocus", true); + this.isActive = true; + } + onBlur() { + if (!this.isActive || !this.node.animationState) + return; + this.node.animationState.setActive("whileFocus", false); + this.isActive = false; + } + mount() { + this.unmount = pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur())); + } + unmount() { } + } + + function handlePressEvent(node, event, lifecycle) { + const { props } = node; + if (node.current instanceof HTMLButtonElement && node.current.disabled) { + return; + } + if (node.animationState && props.whileTap) { + node.animationState.setActive("whileTap", lifecycle === "Start"); + } + const eventName = ("onTap" + (lifecycle === "End" ? "" : lifecycle)); + const callback = props[eventName]; + if (callback) { + frame.postRender(() => callback(event, extractEventInfo(event))); + } + } + class PressGesture extends Feature { + mount() { + const { current } = this.node; + if (!current) + return; + this.unmount = press(current, (_element, startEvent) => { + handlePressEvent(this.node, startEvent, "Start"); + return (endEvent, { success }) => handlePressEvent(this.node, endEvent, success ? "End" : "Cancel"); + }, { useGlobalTarget: this.node.props.globalTapTarget }); + } + unmount() { } + } + + /** + * Map an IntersectionHandler callback to an element. We only ever make one handler for one + * element, so even though these handlers might all be triggered by different + * observers, we can keep them in the same map. + */ + const observerCallbacks = new WeakMap(); + /** + * Multiple observers can be created for multiple element/document roots. Each with + * different settings. So here we store dictionaries of observers to each root, + * using serialised settings (threshold/margin) as lookup keys. + */ + const observers = new WeakMap(); + const fireObserverCallback = (entry) => { + const callback = observerCallbacks.get(entry.target); + callback && callback(entry); + }; + const fireAllObserverCallbacks = (entries) => { + entries.forEach(fireObserverCallback); + }; + function initIntersectionObserver({ root, ...options }) { + const lookupRoot = root || document; + /** + * If we don't have an observer lookup map for this root, create one. + */ + if (!observers.has(lookupRoot)) { + observers.set(lookupRoot, {}); + } + const rootObservers = observers.get(lookupRoot); + const key = JSON.stringify(options); + /** + * If we don't have an observer for this combination of root and settings, + * create one. + */ + if (!rootObservers[key]) { + rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, { root, ...options }); + } + return rootObservers[key]; + } + function observeIntersection(element, options, callback) { + const rootInteresectionObserver = initIntersectionObserver(options); + observerCallbacks.set(element, callback); + rootInteresectionObserver.observe(element); + return () => { + observerCallbacks.delete(element); + rootInteresectionObserver.unobserve(element); + }; + } + + const thresholdNames = { + some: 0, + all: 1, + }; + class InViewFeature extends Feature { + constructor() { + super(...arguments); + this.hasEnteredView = false; + this.isInView = false; + } + startObserver() { + this.unmount(); + const { viewport = {} } = this.node.getProps(); + const { root, margin: rootMargin, amount = "some", once } = viewport; + const options = { + root: root ? root.current : undefined, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholdNames[amount], + }; + const onIntersectionUpdate = (entry) => { + const { isIntersecting } = entry; + /** + * If there's been no change in the viewport state, early return. + */ + if (this.isInView === isIntersecting) + return; + this.isInView = isIntersecting; + /** + * Handle hasEnteredView. If this is only meant to run once, and + * element isn't visible, early return. Otherwise set hasEnteredView to true. + */ + if (once && !isIntersecting && this.hasEnteredView) { + return; + } + else if (isIntersecting) { + this.hasEnteredView = true; + } + if (this.node.animationState) { + this.node.animationState.setActive("whileInView", isIntersecting); + } + /** + * Use the latest committed props rather than the ones in scope + * when this observer is created + */ + const { onViewportEnter, onViewportLeave } = this.node.getProps(); + const callback = isIntersecting ? onViewportEnter : onViewportLeave; + callback && callback(entry); + }; + return observeIntersection(this.node.current, options, onIntersectionUpdate); + } + mount() { + this.startObserver(); + } + update() { + if (typeof IntersectionObserver === "undefined") + return; + const { props, prevProps } = this.node; + const hasOptionsChanged = ["amount", "margin", "root"].some(hasViewportOptionChanged(props, prevProps)); + if (hasOptionsChanged) { + this.startObserver(); + } + } + unmount() { } + } + function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) { + return (name) => viewport[name] !== prevViewport[name]; + } + + const gestureAnimations = { + inView: { + Feature: InViewFeature, + }, + tap: { + Feature: PressGesture, + }, + focus: { + Feature: FocusGesture, + }, + hover: { + Feature: HoverGesture, + }, + }; + + const layout = { + layout: { + ProjectionNode: HTMLProjectionNode, + MeasureLayout, + }, + }; + + const MotionContext = /* @__PURE__ */ React$1.createContext({}); + + function getCurrentTreeVariants(props, context) { + if (isControllingVariants(props)) { + const { initial, animate } = props; + return { + initial: initial === false || isVariantLabel(initial) + ? initial + : undefined, + animate: isVariantLabel(animate) ? animate : undefined, + }; + } + return props.inherit !== false ? context : {}; + } + + function useCreateMotionContext(props) { + const { initial, animate } = getCurrentTreeVariants(props, React$1.useContext(MotionContext)); + return React$1.useMemo(() => ({ initial, animate }), [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]); + } + function variantLabelsAsDependency(prop) { + return Array.isArray(prop) ? prop.join(" ") : prop; + } + + const motionComponentSymbol = Symbol.for("motionComponentSymbol"); + + /** + * Creates a ref function that, when called, hydrates the provided + * external ref and VisualElement. + */ + function useMotionRef(visualState, visualElement, externalRef) { + return React$1.useCallback((instance) => { + if (instance) { + visualState.onMount && visualState.onMount(instance); + } + if (visualElement) { + if (instance) { + visualElement.mount(instance); + } + else { + visualElement.unmount(); + } + } + if (externalRef) { + if (typeof externalRef === "function") { + externalRef(instance); + } + else if (isRefObject(externalRef)) { + externalRef.current = instance; + } + } + }, + /** + * Only pass a new ref callback to React if we've received a visual element + * factory. Otherwise we'll be mounting/remounting every time externalRef + * or other dependencies change. + */ + [visualElement]); + } + + function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor) { + const { visualElement: parent } = React$1.useContext(MotionContext); + const lazyContext = React$1.useContext(LazyContext); + const presenceContext = React$1.useContext(PresenceContext); + const reducedMotionConfig = React$1.useContext(MotionConfigContext).reducedMotion; + const visualElementRef = React$1.useRef(null); + /** + * If we haven't preloaded a renderer, check to see if we have one lazy-loaded + */ + createVisualElement = createVisualElement || lazyContext.renderer; + if (!visualElementRef.current && createVisualElement) { + visualElementRef.current = createVisualElement(Component, { + visualState, + parent, + props, + presenceContext, + blockInitialAnimation: presenceContext + ? presenceContext.initial === false + : false, + reducedMotionConfig, + }); + } + const visualElement = visualElementRef.current; + /** + * Load Motion gesture and animation features. These are rendered as renderless + * components so each feature can optionally make use of React lifecycle methods. + */ + const initialLayoutGroupConfig = React$1.useContext(SwitchLayoutGroupContext); + if (visualElement && + !visualElement.projection && + ProjectionNodeConstructor && + (visualElement.type === "html" || visualElement.type === "svg")) { + createProjectionNode(visualElementRef.current, props, ProjectionNodeConstructor, initialLayoutGroupConfig); + } + const isMounted = React$1.useRef(false); + React$1.useInsertionEffect(() => { + /** + * Check the component has already mounted before calling + * `update` unnecessarily. This ensures we skip the initial update. + */ + if (visualElement && isMounted.current) { + visualElement.update(props, presenceContext); + } + }); + /** + * Cache this value as we want to know whether HandoffAppearAnimations + * was present on initial render - it will be deleted after this. + */ + const optimisedAppearId = props[optimizedAppearDataAttribute]; + const wantsHandoff = React$1.useRef(Boolean(optimisedAppearId) && + !window.MotionHandoffIsComplete?.(optimisedAppearId) && + window.MotionHasOptimisedAnimation?.(optimisedAppearId)); + useIsomorphicLayoutEffect(() => { + if (!visualElement) + return; + isMounted.current = true; + window.MotionIsMounted = true; + visualElement.updateFeatures(); + microtask.render(visualElement.render); + /** + * Ideally this function would always run in a useEffect. + * + * However, if we have optimised appear animations to handoff from, + * it needs to happen synchronously to ensure there's no flash of + * incorrect styles in the event of a hydration error. + * + * So if we detect a situtation where optimised appear animations + * are running, we use useLayoutEffect to trigger animations. + */ + if (wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + }); + React$1.useEffect(() => { + if (!visualElement) + return; + if (!wantsHandoff.current && visualElement.animationState) { + visualElement.animationState.animateChanges(); + } + if (wantsHandoff.current) { + // This ensures all future calls to animateChanges() in this component will run in useEffect + queueMicrotask(() => { + window.MotionHandoffMarkAsComplete?.(optimisedAppearId); + }); + wantsHandoff.current = false; + } + }); + return visualElement; + } + function createProjectionNode(visualElement, props, ProjectionNodeConstructor, initialPromotionConfig) { + const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, layoutCrossfade, } = props; + visualElement.projection = new ProjectionNodeConstructor(visualElement.latestValues, props["data-framer-portal-id"] + ? undefined + : getClosestProjectingNode(visualElement.parent)); + visualElement.projection.setOptions({ + layoutId, + layout, + alwaysMeasureLayout: Boolean(drag) || (dragConstraints && isRefObject(dragConstraints)), + visualElement, + /** + * TODO: Update options in an effect. This could be tricky as it'll be too late + * to update by the time layout animations run. + * We also need to fix this safeToRemove by linking it up to the one returned by usePresence, + * ensuring it gets called if there's no potential layout animations. + * + */ + animationType: typeof layout === "string" ? layout : "both", + initialPromotionConfig, + crossfade: layoutCrossfade, + layoutScroll, + layoutRoot, + }); + } + function getClosestProjectingNode(visualElement) { + if (!visualElement) + return undefined; + return visualElement.options.allowProjection !== false + ? visualElement.projection + : getClosestProjectingNode(visualElement.parent); + } + + /** + * Create a `motion` component. + * + * This function accepts a Component argument, which can be either a string (ie "div" + * for `motion.div`), or an actual React component. + * + * Alongside this is a config option which provides a way of rendering the provided + * component "offline", or outside the React render cycle. + */ + function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) { + preloadedFeatures && loadFeatures(preloadedFeatures); + function MotionComponent(props, externalRef) { + /** + * If we need to measure the element we load this functionality in a + * separate class component in order to gain access to getSnapshotBeforeUpdate. + */ + let MeasureLayout; + const configAndProps = { + ...React$1.useContext(MotionConfigContext), + ...props, + layoutId: useLayoutId(props), + }; + const { isStatic } = configAndProps; + const context = useCreateMotionContext(props); + const visualState = useVisualState(props, isStatic); + if (!isStatic && isBrowser) { + useStrictMode(configAndProps, preloadedFeatures); + const layoutProjection = getProjectionFunctionality(configAndProps); + MeasureLayout = layoutProjection.MeasureLayout; + /** + * Create a VisualElement for this component. A VisualElement provides a common + * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as + * providing a way of rendering to these APIs outside of the React render loop + * for more performant animations and interactions + */ + context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement, layoutProjection.ProjectionNode); + } + /** + * The mount order and hierarchy is specific to ensure our element ref + * is hydrated by the time features fire their effects. + */ + return (jsxs(MotionContext.Provider, { value: context, children: [MeasureLayout && context.visualElement ? (jsx(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null, useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)] })); + } + MotionComponent.displayName = `motion.${typeof Component === "string" + ? Component + : `create(${Component.displayName ?? Component.name ?? ""})`}`; + const ForwardRefMotionComponent = React$1.forwardRef(MotionComponent); + ForwardRefMotionComponent[motionComponentSymbol] = Component; + return ForwardRefMotionComponent; + } + function useLayoutId({ layoutId }) { + const layoutGroupId = React$1.useContext(LayoutGroupContext).id; + return layoutGroupId && layoutId !== undefined + ? layoutGroupId + "-" + layoutId + : layoutId; + } + function useStrictMode(configAndProps, preloadedFeatures) { + const isStrict = React$1.useContext(LazyContext).strict; + /** + * If we're in development mode, check to make sure we're not rendering a motion component + * as a child of LazyMotion, as this will break the file-size benefits of using it. + */ + if (preloadedFeatures && + isStrict) { + const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead."; + configAndProps.ignoreStrict + ? exports.warning(false, strictMessage) + : exports.invariant(false, strictMessage); + } + } + function getProjectionFunctionality(props) { + const { drag, layout } = featureDefinitions; + if (!drag && !layout) + return {}; + const combined = { ...drag, ...layout }; + return { + MeasureLayout: drag?.isEnabled(props) || layout?.isEnabled(props) + ? combined.MeasureLayout + : undefined, + ProjectionNode: combined.ProjectionNode, + }; + } + + const createHtmlRenderState = () => ({ + style: {}, + transform: {}, + transformOrigin: {}, + vars: {}, + }); + + function copyRawValuesOnly(target, source, props) { + for (const key in source) { + if (!isMotionValue(source[key]) && !isForcedMotionValue(key, props)) { + target[key] = source[key]; + } + } + } + function useInitialMotionValues({ transformTemplate }, visualState) { + return React$1.useMemo(() => { + const state = createHtmlRenderState(); + buildHTMLStyles(state, visualState, transformTemplate); + return Object.assign({}, state.vars, state.style); + }, [visualState]); + } + function useStyle(props, visualState) { + const styleProp = props.style || {}; + const style = {}; + /** + * Copy non-Motion Values straight into style + */ + copyRawValuesOnly(style, styleProp, props); + Object.assign(style, useInitialMotionValues(props, visualState)); + return style; + } + function useHTMLProps(props, visualState) { + // The `any` isn't ideal but it is the type of createElement props argument + const htmlProps = {}; + const style = useStyle(props, visualState); + if (props.drag && props.dragListener !== false) { + // Disable the ghost element when a user drags + htmlProps.draggable = false; + // Disable text selection + style.userSelect = + style.WebkitUserSelect = + style.WebkitTouchCallout = + "none"; + // Disable scrolling on the draggable direction + style.touchAction = + props.drag === true + ? "none" + : `pan-${props.drag === "x" ? "y" : "x"}`; + } + if (props.tabIndex === undefined && + (props.onTap || props.onTapStart || props.whileTap)) { + htmlProps.tabIndex = 0; + } + htmlProps.style = style; + return htmlProps; + } + + const dashKeys = { + offset: "stroke-dashoffset", + array: "stroke-dasharray", + }; + const camelKeys = { + offset: "strokeDashoffset", + array: "strokeDasharray", + }; + /** + * Build SVG path properties. Uses the path's measured length to convert + * our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset + * and stroke-dasharray attributes. + * + * This function is mutative to reduce per-frame GC. + */ + function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) { + // Normalise path length by setting SVG attribute pathLength to 1 + attrs.pathLength = 1; + // We use dash case when setting attributes directly to the DOM node and camel case + // when defining props on a React component. + const keys = useDashCase ? dashKeys : camelKeys; + // Build the dash offset + attrs[keys.offset] = px.transform(-offset); + // Build the dash array + const pathLength = px.transform(length); + const pathSpacing = px.transform(spacing); + attrs[keys.array] = `${pathLength} ${pathSpacing}`; + } + + /** + * Build SVG visual attributes, like cx and style.transform + */ + function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0, + // This is object creation, which we try to avoid per-frame. + ...latest }, isSVGTag, transformTemplate, styleProp) { + buildHTMLStyles(state, latest, transformTemplate); + /** + * For svg tags we just want to make sure viewBox is animatable and treat all the styles + * as normal HTML tags. + */ + if (isSVGTag) { + if (state.style.viewBox) { + state.attrs.viewBox = state.style.viewBox; + } + return; + } + state.attrs = state.style; + state.style = {}; + const { attrs, style } = state; + /** + * However, we apply transforms as CSS transforms. + * So if we detect a transform, transformOrigin we take it from attrs and copy it into style. + */ + if (attrs.transform) { + style.transform = attrs.transform; + delete attrs.transform; + } + if (style.transform || attrs.transformOrigin) { + style.transformOrigin = attrs.transformOrigin ?? "50% 50%"; + delete attrs.transformOrigin; + } + if (style.transform) { + /** + * SVG's element transform-origin uses its own median as a reference. + * Therefore, transformBox becomes a fill-box + */ + style.transformBox = styleProp?.transformBox ?? "fill-box"; + delete attrs.transformBox; + } + // Render attrX/attrY/attrScale as attributes + if (attrX !== undefined) + attrs.x = attrX; + if (attrY !== undefined) + attrs.y = attrY; + if (attrScale !== undefined) + attrs.scale = attrScale; + // Build SVG path if one has been defined + if (pathLength !== undefined) { + buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false); + } + } + + const createSvgRenderState = () => ({ + ...createHtmlRenderState(), + attrs: {}, + }); + + const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg"; + + function useSVGProps(props, visualState, _isStatic, Component) { + const visualProps = React$1.useMemo(() => { + const state = createSvgRenderState(); + buildSVGAttrs(state, visualState, isSVGTag(Component), props.transformTemplate, props.style); + return { + ...state.attrs, + style: { ...state.style }, + }; + }, [visualState]); + if (props.style) { + const rawStyles = {}; + copyRawValuesOnly(rawStyles, props.style, props); + visualProps.style = { ...rawStyles, ...visualProps.style }; + } + return visualProps; + } + + /** + * We keep these listed separately as we use the lowercase tag names as part + * of the runtime bundle to detect SVG components + */ + const lowercaseSVGElements = [ + "animate", + "circle", + "defs", + "desc", + "ellipse", + "g", + "image", + "line", + "filter", + "marker", + "mask", + "metadata", + "path", + "pattern", + "polygon", + "polyline", + "rect", + "stop", + "switch", + "symbol", + "svg", + "text", + "tspan", + "use", + "view", + ]; + + function isSVGComponent(Component) { + if ( + /** + * If it's not a string, it's a custom React component. Currently we only support + * HTML custom React components. + */ + typeof Component !== "string" || + /** + * If it contains a dash, the element is a custom HTML webcomponent. + */ + Component.includes("-")) { + return false; + } + else if ( + /** + * If it's in our list of lowercase SVG tags, it's an SVG component + */ + lowercaseSVGElements.indexOf(Component) > -1 || + /** + * If it contains a capital letter, it's an SVG component + */ + /[A-Z]/u.test(Component)) { + return true; + } + return false; + } + + function createUseRender(forwardMotionProps = false) { + const useRender = (Component, props, ref, { latestValues }, isStatic) => { + const useVisualProps = isSVGComponent(Component) + ? useSVGProps + : useHTMLProps; + const visualProps = useVisualProps(props, latestValues, isStatic, Component); + const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps); + const elementProps = Component !== React$1.Fragment + ? { ...filteredProps, ...visualProps, ref } + : {}; + /** + * If component has been handed a motion value as its child, + * memoise its initial value and render that. Subsequent updates + * will be handled by the onChange handler + */ + const { children } = props; + const renderedChildren = React$1.useMemo(() => (isMotionValue(children) ? children.get() : children), [children]); + return React$1.createElement(Component, { + ...elementProps, + children: renderedChildren, + }); + }; + return useRender; + } + + function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) { + const state = { + latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps), + renderState: createRenderState(), + }; + return state; + } + const makeUseVisualState = (config) => (props, isStatic) => { + const context = React$1.useContext(MotionContext); + const presenceContext = React$1.useContext(PresenceContext); + const make = () => makeState(config, props, context, presenceContext); + return isStatic ? make() : useConstant(make); + }; + function makeLatestValues(props, context, presenceContext, scrapeMotionValues) { + const values = {}; + const motionValues = scrapeMotionValues(props, {}); + for (const key in motionValues) { + values[key] = resolveMotionValue(motionValues[key]); + } + let { initial, animate } = props; + const isControllingVariants$1 = isControllingVariants(props); + const isVariantNode$1 = isVariantNode(props); + if (context && + isVariantNode$1 && + !isControllingVariants$1 && + props.inherit !== false) { + if (initial === undefined) + initial = context.initial; + if (animate === undefined) + animate = context.animate; + } + let isInitialAnimationBlocked = presenceContext + ? presenceContext.initial === false + : false; + isInitialAnimationBlocked = isInitialAnimationBlocked || initial === false; + const variantToSet = isInitialAnimationBlocked ? animate : initial; + if (variantToSet && + typeof variantToSet !== "boolean" && + !isAnimationControls(variantToSet)) { + const list = Array.isArray(variantToSet) ? variantToSet : [variantToSet]; + for (let i = 0; i < list.length; i++) { + const resolved = resolveVariantFromProps(props, list[i]); + if (resolved) { + const { transitionEnd, transition, ...target } = resolved; + for (const key in target) { + let valueTarget = target[key]; + if (Array.isArray(valueTarget)) { + /** + * Take final keyframe if the initial animation is blocked because + * we want to initialise at the end of that blocked animation. + */ + const index = isInitialAnimationBlocked + ? valueTarget.length - 1 + : 0; + valueTarget = valueTarget[index]; + } + if (valueTarget !== null) { + values[key] = valueTarget; + } + } + for (const key in transitionEnd) { + values[key] = transitionEnd[key]; + } + } + } + } + return values; + } + + const htmlMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps$1, + createRenderState: createHtmlRenderState, + }), + }; + + function scrapeMotionValuesFromProps(props, prevProps, visualElement) { + const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement); + for (const key in props) { + if (isMotionValue(props[key]) || + isMotionValue(prevProps[key])) { + const targetKey = transformPropOrder.indexOf(key) !== -1 + ? "attr" + key.charAt(0).toUpperCase() + key.substring(1) + : key; + newValues[targetKey] = props[key]; + } + } + return newValues; + } + + const svgMotionConfig = { + useVisualState: makeUseVisualState({ + scrapeMotionValuesFromProps: scrapeMotionValuesFromProps, + createRenderState: createSvgRenderState, + }), + }; + + function createMotionComponentFactory(preloadedFeatures, createVisualElement) { + return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) { + const baseConfig = isSVGComponent(Component) + ? svgMotionConfig + : htmlMotionConfig; + const config = { + ...baseConfig, + preloadedFeatures, + useRender: createUseRender(forwardMotionProps), + createVisualElement, + Component, + }; + return createRendererMotionComponent(config); + }; + } + + /** + * A set of attribute names that are always read/written as camel case. + */ + const camelCaseAttributes = new Set([ + "baseFrequency", + "diffuseConstant", + "kernelMatrix", + "kernelUnitLength", + "keySplines", + "keyTimes", + "limitingConeAngle", + "markerHeight", + "markerWidth", + "numOctaves", + "targetX", + "targetY", + "surfaceScale", + "specularConstant", + "specularExponent", + "stdDeviation", + "tableValues", + "viewBox", + "gradientTransform", + "pathLength", + "startOffset", + "textLength", + "lengthAdjust", + ]); + + function renderSVG(element, renderState, _styleProp, projection) { + renderHTML(element, renderState, undefined, projection); + for (const key in renderState.attrs) { + element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]); + } + } + + class SVGVisualElement extends DOMVisualElement { + constructor() { + super(...arguments); + this.type = "svg"; + this.isSVGTag = false; + this.measureInstanceViewportBox = createBox; + } + getBaseTargetFromProps(props, key) { + return props[key]; + } + readValueFromInstance(instance, key) { + if (transformProps.has(key)) { + const defaultType = getDefaultValueType(key); + return defaultType ? defaultType.default || 0 : 0; + } + key = !camelCaseAttributes.has(key) ? camelToDash(key) : key; + return instance.getAttribute(key); + } + scrapeMotionValuesFromProps(props, prevProps, visualElement) { + return scrapeMotionValuesFromProps(props, prevProps, visualElement); + } + build(renderState, latestValues, props) { + buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate, props.style); + } + renderInstance(instance, renderState, styleProp, projection) { + renderSVG(instance, renderState, styleProp, projection); + } + mount(instance) { + this.isSVGTag = isSVGTag(instance.tagName); + super.mount(instance); + } + } + + const createDomVisualElement = (Component, options) => { + return isSVGComponent(Component) + ? new SVGVisualElement(options) + : new HTMLVisualElement(options, { + allowProjection: Component !== React$1.Fragment, + }); + }; + + const createMotionComponent = /*@__PURE__*/ createMotionComponentFactory({ + ...animations, + ...gestureAnimations, + ...drag, + ...layout, + }, createDomVisualElement); + + const motion = /*@__PURE__*/ createDOMMotionComponentProxy(createMotionComponent); + + function checkReorder(order, value, offset, velocity) { + if (!velocity) + return order; + const index = order.findIndex((item) => item.value === value); + if (index === -1) + return order; + const nextOffset = velocity > 0 ? 1 : -1; + const nextItem = order[index + nextOffset]; + if (!nextItem) + return order; + const item = order[index]; + const nextLayout = nextItem.layout; + const nextItemCenter = mixNumber$1(nextLayout.min, nextLayout.max, 0.5); + if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) || + (nextOffset === -1 && item.layout.min + offset < nextItemCenter)) { + return moveItem(order, index, index + nextOffset); + } + return order; + } + + function ReorderGroupComponent({ children, as = "ul", axis = "y", onReorder, values, ...props }, externalRef) { + const Component = useConstant(() => motion[as]); + const order = []; + const isReordering = React$1.useRef(false); + exports.invariant(Boolean(values), "Reorder.Group must be provided a values prop"); + const context = { + axis, + registerItem: (value, layout) => { + // If the entry was already added, update it rather than adding it again + const idx = order.findIndex((entry) => value === entry.value); + if (idx !== -1) { + order[idx].layout = layout[axis]; + } + else { + order.push({ value: value, layout: layout[axis] }); + } + order.sort(compareMin); + }, + updateOrder: (item, offset, velocity) => { + if (isReordering.current) + return; + const newOrder = checkReorder(order, item, offset, velocity); + if (order !== newOrder) { + isReordering.current = true; + onReorder(newOrder + .map(getValue) + .filter((value) => values.indexOf(value) !== -1)); + } + }, + }; + React$1.useEffect(() => { + isReordering.current = false; + }); + return (jsx(Component, { ...props, ref: externalRef, ignoreStrict: true, children: jsx(ReorderContext.Provider, { value: context, children: children }) })); + } + const ReorderGroup = /*@__PURE__*/ React$1.forwardRef(ReorderGroupComponent); + function getValue(item) { + return item.value; + } + function compareMin(a, b) { + return a.layout.min - b.layout.min; + } + + /** + * Creates a `MotionValue` to track the state and velocity of a value. + * + * Usually, these are created automatically. For advanced use-cases, like use with `useTransform`, you can create `MotionValue`s externally and pass them into the animated component via the `style` prop. + * + * ```jsx + * export const MyComponent = () => { + * const scale = useMotionValue(1) + * + * return + * } + * ``` + * + * @param initial - The initial state. + * + * @public + */ + function useMotionValue(initial) { + const value = useConstant(() => motionValue(initial)); + /** + * If this motion value is being used in static mode, like on + * the Framer canvas, force components to rerender when the motion + * value is updated. + */ + const { isStatic } = React$1.useContext(MotionConfigContext); + if (isStatic) { + const [, setLatest] = React$1.useState(initial); + React$1.useEffect(() => value.on("change", setLatest), []); + } + return value; + } + + function useCombineMotionValues(values, combineValues) { + /** + * Initialise the returned motion value. This remains the same between renders. + */ + const value = useMotionValue(combineValues()); + /** + * Create a function that will update the template motion value with the latest values. + * This is pre-bound so whenever a motion value updates it can schedule its + * execution in Framesync. If it's already been scheduled it won't be fired twice + * in a single frame. + */ + const updateValue = () => value.set(combineValues()); + /** + * Synchronously update the motion value with the latest values during the render. + * This ensures that within a React render, the styles applied to the DOM are up-to-date. + */ + updateValue(); + /** + * Subscribe to all motion values found within the template. Whenever any of them change, + * schedule an update. + */ + useIsomorphicLayoutEffect(() => { + const scheduleUpdate = () => frame.preRender(updateValue, false, true); + const subscriptions = values.map((v) => v.on("change", scheduleUpdate)); + return () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + cancelFrame(updateValue); + }; + }); + return value; + } + + function useComputed(compute) { + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * will be saved into this array. + */ + collectMotionValues.current = []; + compute(); + const value = useCombineMotionValues(collectMotionValues.current, compute); + /** + * Synchronously close session of collectMotionValues. + */ + collectMotionValues.current = undefined; + return value; + } + + function useTransform(input, inputRangeOrTransformer, outputRange, options) { + if (typeof input === "function") { + return useComputed(input); + } + const transformer = typeof inputRangeOrTransformer === "function" + ? inputRangeOrTransformer + : transform(inputRangeOrTransformer, outputRange, options); + return Array.isArray(input) + ? useListTransform(input, transformer) + : useListTransform([input], ([latest]) => transformer(latest)); + } + function useListTransform(values, transformer) { + const latest = useConstant(() => []); + return useCombineMotionValues(values, () => { + latest.length = 0; + const numValues = values.length; + for (let i = 0; i < numValues; i++) { + latest[i] = values[i].get(); + } + return transformer(latest); + }); + } + + function useDefaultMotionValue(value, defaultValue = 0) { + return isMotionValue(value) ? value : useMotionValue(defaultValue); + } + function ReorderItemComponent({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) { + const Component = useConstant(() => motion[as]); + const context = React$1.useContext(ReorderContext); + const point = { + x: useDefaultMotionValue(style.x), + y: useDefaultMotionValue(style.y), + }; + const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset"); + exports.invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group"); + const { axis, registerItem, updateOrder } = context; + return (jsx(Component, { drag: axis, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDrag: (event, gesturePoint) => { + const { velocity } = gesturePoint; + velocity[axis] && + updateOrder(value, point[axis].get(), velocity[axis]); + onDrag && onDrag(event, gesturePoint); + }, onLayoutMeasure: (measured) => registerItem(value, measured), ref: externalRef, ignoreStrict: true, children: children })); + } + const ReorderItem = /*@__PURE__*/ React$1.forwardRef(ReorderItemComponent); + + var namespace = /*#__PURE__*/Object.freeze({ + __proto__: null, + Group: ReorderGroup, + Item: ReorderItem + }); + + function isDOMKeyframes(keyframes) { + return typeof keyframes === "object" && !Array.isArray(keyframes); + } + + function resolveSubjects(subject, keyframes, scope, selectorCache) { + if (typeof subject === "string" && isDOMKeyframes(keyframes)) { + return resolveElements(subject, scope, selectorCache); + } + else if (subject instanceof NodeList) { + return Array.from(subject); + } + else if (Array.isArray(subject)) { + return subject; + } + else { + return [subject]; + } + } + + function calculateRepeatDuration(duration, repeat, _repeatDelay) { + return duration * (repeat + 1); + } + + /** + * Given a absolute or relative time definition and current/prev time state of the sequence, + * calculate an absolute time for the next keyframes. + */ + function calcNextTime(current, next, prev, labels) { + if (typeof next === "number") { + return next; + } + else if (next.startsWith("-") || next.startsWith("+")) { + return Math.max(0, current + parseFloat(next)); + } + else if (next === "<") { + return prev; + } + else if (next.startsWith("<")) { + return Math.max(0, prev + parseFloat(next.slice(1))); + } + else { + return labels.get(next) ?? current; + } + } + + function eraseKeyframes(sequence, startTime, endTime) { + for (let i = 0; i < sequence.length; i++) { + const keyframe = sequence[i]; + if (keyframe.at > startTime && keyframe.at < endTime) { + removeItem(sequence, keyframe); + // If we remove this item we have to push the pointer back one + i--; + } + } + } + function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) { + /** + * Erase every existing value between currentTime and targetTime, + * this will essentially splice this timeline into any currently + * defined ones. + */ + eraseKeyframes(sequence, startTime, endTime); + for (let i = 0; i < keyframes.length; i++) { + sequence.push({ + value: keyframes[i], + at: mixNumber$1(startTime, endTime, offset[i]), + easing: getEasingForSegment(easing, i), + }); + } + } + + /** + * Take an array of times that represent repeated keyframes. For instance + * if we have original times of [0, 0.5, 1] then our repeated times will + * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back + * down to a 0-1 scale. + */ + function normalizeTimes(times, repeat) { + for (let i = 0; i < times.length; i++) { + times[i] = times[i] / (repeat + 1); + } + } + + function compareByTime(a, b) { + if (a.at === b.at) { + if (a.value === null) + return 1; + if (b.value === null) + return -1; + return 0; + } + else { + return a.at - b.at; + } + } + + const defaultSegmentEasing = "easeInOut"; + const MAX_REPEAT = 20; + function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) { + const defaultDuration = defaultTransition.duration || 0.3; + const animationDefinitions = new Map(); + const sequences = new Map(); + const elementCache = {}; + const timeLabels = new Map(); + let prevTime = 0; + let currentTime = 0; + let totalDuration = 0; + /** + * Build the timeline by mapping over the sequence array and converting + * the definitions into keyframes and offsets with absolute time values. + * These will later get converted into relative offsets in a second pass. + */ + for (let i = 0; i < sequence.length; i++) { + const segment = sequence[i]; + /** + * If this is a timeline label, mark it and skip the rest of this iteration. + */ + if (typeof segment === "string") { + timeLabels.set(segment, currentTime); + continue; + } + else if (!Array.isArray(segment)) { + timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels)); + continue; + } + let [subject, keyframes, transition = {}] = segment; + /** + * If a relative or absolute time value has been specified we need to resolve + * it in relation to the currentTime. + */ + if (transition.at !== undefined) { + currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels); + } + /** + * Keep track of the maximum duration in this definition. This will be + * applied to currentTime once the definition has been parsed. + */ + let maxDuration = 0; + const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => { + const valueKeyframesAsList = keyframesAsList(valueKeyframes); + const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition; + let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition; + /** + * Resolve stagger() if defined. + */ + const calculatedDelay = typeof delay === "function" + ? delay(elementIndex, numSubjects) + : delay; + /** + * If this animation should and can use a spring, generate a spring easing function. + */ + const numKeyframes = valueKeyframesAsList.length; + const createGenerator = isGenerator(type) + ? type + : generators?.[type || "keyframes"]; + if (numKeyframes <= 2 && createGenerator) { + /** + * As we're creating an easing function from a spring, + * ideally we want to generate it using the real distance + * between the two keyframes. However this isn't always + * possible - in these situations we use 0-100. + */ + let absoluteDelta = 100; + if (numKeyframes === 2 && + isNumberKeyframesArray(valueKeyframesAsList)) { + const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0]; + absoluteDelta = Math.abs(delta); + } + const springTransition = { ...remainingTransition }; + if (duration !== undefined) { + springTransition.duration = secondsToMilliseconds(duration); + } + const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator); + ease = springEasing.ease; + duration = springEasing.duration; + } + duration ?? (duration = defaultDuration); + const startTime = currentTime + calculatedDelay; + /** + * If there's only one time offset of 0, fill in a second with length 1 + */ + if (times.length === 1 && times[0] === 0) { + times[1] = 1; + } + /** + * Fill out if offset if fewer offsets than keyframes + */ + const remainder = times.length - valueKeyframesAsList.length; + remainder > 0 && fillOffset(times, remainder); + /** + * If only one value has been set, ie [1], push a null to the start of + * the keyframe array. This will let us mark a keyframe at this point + * that will later be hydrated with the previous value. + */ + valueKeyframesAsList.length === 1 && + valueKeyframesAsList.unshift(null); + /** + * Handle repeat options + */ + if (repeat) { + exports.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20"); + duration = calculateRepeatDuration(duration, repeat); + const originalKeyframes = [...valueKeyframesAsList]; + const originalTimes = [...times]; + ease = Array.isArray(ease) ? [...ease] : [ease]; + const originalEase = [...ease]; + for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { + valueKeyframesAsList.push(...originalKeyframes); + for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) { + times.push(originalTimes[keyframeIndex] + (repeatIndex + 1)); + ease.push(keyframeIndex === 0 + ? "linear" + : getEasingForSegment(originalEase, keyframeIndex - 1)); + } + } + normalizeTimes(times, repeat); + } + const targetTime = startTime + duration; + /** + * Add keyframes, mapping offsets to absolute time. + */ + addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime); + maxDuration = Math.max(calculatedDelay + duration, maxDuration); + totalDuration = Math.max(targetTime, totalDuration); + }; + if (isMotionValue(subject)) { + const subjectSequence = getSubjectSequence(subject, sequences); + resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope, elementCache); + const numSubjects = subjects.length; + /** + * For every element in this segment, process the defined values. + */ + for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) { + /** + * Cast necessary, but we know these are of this type + */ + keyframes = keyframes; + transition = transition; + const thisSubject = subjects[subjectIndex]; + const subjectSequence = getSubjectSequence(thisSubject, sequences); + for (const key in keyframes) { + resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects); + } + } + } + prevTime = currentTime; + currentTime += maxDuration; + } + /** + * For every element and value combination create a new animation. + */ + sequences.forEach((valueSequences, element) => { + for (const key in valueSequences) { + const valueSequence = valueSequences[key]; + /** + * Arrange all the keyframes in ascending time order. + */ + valueSequence.sort(compareByTime); + const keyframes = []; + const valueOffset = []; + const valueEasing = []; + /** + * For each keyframe, translate absolute times into + * relative offsets based on the total duration of the timeline. + */ + for (let i = 0; i < valueSequence.length; i++) { + const { at, value, easing } = valueSequence[i]; + keyframes.push(value); + valueOffset.push(progress(0, totalDuration, at)); + valueEasing.push(easing || "easeOut"); + } + /** + * If the first keyframe doesn't land on offset: 0 + * provide one by duplicating the initial keyframe. This ensures + * it snaps to the first keyframe when the animation starts. + */ + if (valueOffset[0] !== 0) { + valueOffset.unshift(0); + keyframes.unshift(keyframes[0]); + valueEasing.unshift(defaultSegmentEasing); + } + /** + * If the last keyframe doesn't land on offset: 1 + * provide one with a null wildcard value. This will ensure it + * stays static until the end of the animation. + */ + if (valueOffset[valueOffset.length - 1] !== 1) { + valueOffset.push(1); + keyframes.push(null); + } + if (!animationDefinitions.has(element)) { + animationDefinitions.set(element, { + keyframes: {}, + transition: {}, + }); + } + const definition = animationDefinitions.get(element); + definition.keyframes[key] = keyframes; + definition.transition[key] = { + ...defaultTransition, + duration: totalDuration, + ease: valueEasing, + times: valueOffset, + ...sequenceTransition, + }; + } + }); + return animationDefinitions; + } + function getSubjectSequence(subject, sequences) { + !sequences.has(subject) && sequences.set(subject, {}); + return sequences.get(subject); + } + function getValueSequence(name, sequences) { + if (!sequences[name]) + sequences[name] = []; + return sequences[name]; + } + function keyframesAsList(keyframes) { + return Array.isArray(keyframes) ? keyframes : [keyframes]; + } + function getValueTransition(transition, key) { + return transition && transition[key] + ? { + ...transition, + ...transition[key], + } + : { ...transition }; + } + const isNumber = (keyframe) => typeof keyframe === "number"; + const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber); + + function isObjectKey(key, object) { + return key in object; + } + class ObjectVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.type = "object"; + } + readValueFromInstance(instance, key) { + if (isObjectKey(key, instance)) { + const value = instance[key]; + if (typeof value === "string" || typeof value === "number") { + return value; + } + } + return undefined; + } + getBaseTargetFromProps() { + return undefined; + } + removeValueFromRenderState(key, renderState) { + delete renderState.output[key]; + } + measureInstanceViewportBox() { + return createBox(); + } + build(renderState, latestValues) { + Object.assign(renderState.output, latestValues); + } + renderInstance(instance, { output }) { + Object.assign(instance, output); + } + sortInstanceNodePosition() { + return 0; + } + } + + function createDOMVisualElement(element) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + transform: {}, + transformOrigin: {}, + style: {}, + vars: {}, + attrs: {}, + }, + latestValues: {}, + }, + }; + const node = isSVGElement(element) && !isSVGSVGElement(element) + ? new SVGVisualElement(options) + : new HTMLVisualElement(options); + node.mount(element); + visualElementStore.set(element, node); + } + function createObjectVisualElement(subject) { + const options = { + presenceContext: null, + props: {}, + visualState: { + renderState: { + output: {}, + }, + latestValues: {}, + }, + }; + const node = new ObjectVisualElement(options); + node.mount(subject); + visualElementStore.set(subject, node); + } + + function isSingleValue(subject, keyframes) { + return (isMotionValue(subject) || + typeof subject === "number" || + (typeof subject === "string" && !isDOMKeyframes(keyframes))); + } + /** + * Implementation + */ + function animateSubject(subject, keyframes, options, scope) { + const animations = []; + if (isSingleValue(subject, keyframes)) { + animations.push(animateSingleValue(subject, isDOMKeyframes(keyframes) + ? keyframes.default || keyframes + : keyframes, options ? options.default || options : options)); + } + else { + const subjects = resolveSubjects(subject, keyframes, scope); + const numSubjects = subjects.length; + exports.invariant(Boolean(numSubjects), "No valid elements provided."); + for (let i = 0; i < numSubjects; i++) { + const thisSubject = subjects[i]; + const createVisualElement = thisSubject instanceof Element + ? createDOMVisualElement + : createObjectVisualElement; + if (!visualElementStore.has(thisSubject)) { + createVisualElement(thisSubject); + } + const visualElement = visualElementStore.get(thisSubject); + const transition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if ("delay" in transition && + typeof transition.delay === "function") { + transition.delay = transition.delay(i, numSubjects); + } + animations.push(...animateTarget(visualElement, { ...keyframes, transition }, {})); + } + } + return animations; + } + + function animateSequence(sequence, options, scope) { + const animations = []; + const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring }); + animationDefinitions.forEach(({ keyframes, transition }, subject) => { + animations.push(...animateSubject(subject, keyframes, transition)); + }); + return animations; + } + + function isSequence(value) { + return Array.isArray(value) && value.some(Array.isArray); + } + /** + * Creates an animation function that is optionally scoped + * to a specific element. + */ + function createScopedAnimate(scope) { + /** + * Implementation + */ + function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) { + let animations = []; + if (isSequence(subjectOrSequence)) { + animations = animateSequence(subjectOrSequence, optionsOrKeyframes, scope); + } + else { + animations = animateSubject(subjectOrSequence, optionsOrKeyframes, options, scope); + } + const animation = new GroupAnimationWithThen(animations); + if (scope) { + scope.animations.push(animation); + animation.finished.then(() => { + removeItem(scope.animations, animation); + }); + } + return animation; + } + return scopedAnimate; + } + const animate = createScopedAnimate(); + + function animateElements(elementOrSelector, keyframes, options, scope) { + const elements = resolveElements(elementOrSelector, scope); + const numElements = elements.length; + exports.invariant(Boolean(numElements), "No valid element provided."); + /** + * WAAPI doesn't support interrupting animations. + * + * Therefore, starting animations requires a three-step process: + * 1. Stop existing animations (write styles to DOM) + * 2. Resolve keyframes (read styles from DOM) + * 3. Create new animations (write styles to DOM) + * + * The hybrid `animate()` function uses AsyncAnimation to resolve + * keyframes before creating new animations, which removes style + * thrashing. Here, we have much stricter filesize constraints. + * Therefore we do this in a synchronous way that ensures that + * at least within `animate()` calls there is no style thrashing. + * + * In the motion-native-animate-mini-interrupt benchmark this + * was 80% faster than a single loop. + */ + const animationDefinitions = []; + /** + * Step 1: Build options and stop existing animations (write) + */ + for (let i = 0; i < numElements; i++) { + const element = elements[i]; + const elementTransition = { ...options }; + /** + * Resolve stagger function if provided. + */ + if (typeof elementTransition.delay === "function") { + elementTransition.delay = elementTransition.delay(i, numElements); + } + for (const valueName in keyframes) { + let valueKeyframes = keyframes[valueName]; + if (!Array.isArray(valueKeyframes)) { + valueKeyframes = [valueKeyframes]; + } + const valueOptions = { + ...getValueTransition$1(elementTransition, valueName), + }; + valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay)); + /** + * If there's an existing animation playing on this element then stop it + * before creating a new one. + */ + const map = getAnimationMap(element); + const key = animationMapKey(valueName, valueOptions.pseudoElement || ""); + const currentAnimation = map.get(key); + currentAnimation && currentAnimation.stop(); + animationDefinitions.push({ + map, + key, + unresolvedKeyframes: valueKeyframes, + options: { + ...valueOptions, + element, + name: valueName, + allowFlatten: !elementTransition.type && !elementTransition.ease, + }, + }); + } + } + /** + * Step 2: Resolve keyframes (read) + */ + for (let i = 0; i < animationDefinitions.length; i++) { + const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i]; + const { element, name, pseudoElement } = animationOptions; + if (!pseudoElement && unresolvedKeyframes[0] === null) { + unresolvedKeyframes[0] = getComputedStyle$2(element, name); + } + fillWildcards(unresolvedKeyframes); + applyPxDefaults(unresolvedKeyframes, name); + /** + * If we only have one keyframe, explicitly read the initial keyframe + * from the computed style. This is to ensure consistency with WAAPI behaviour + * for restarting animations, for instance .play() after finish, when it + * has one vs two keyframes. + */ + if (!pseudoElement && unresolvedKeyframes.length < 2) { + unresolvedKeyframes.unshift(getComputedStyle$2(element, name)); + } + animationOptions.keyframes = unresolvedKeyframes; + } + /** + * Step 3: Create new animations (write) + */ + const animations = []; + for (let i = 0; i < animationDefinitions.length; i++) { + const { map, key, options: animationOptions } = animationDefinitions[i]; + const animation = new NativeAnimation(animationOptions); + map.set(key, animation); + animation.finished.finally(() => map.delete(key)); + animations.push(animation); + } + return animations; + } + + const createScopedWaapiAnimate = (scope) => { + function scopedAnimate(elementOrSelector, keyframes, options) { + return new GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope)); + } + return scopedAnimate; + }; + const animateMini = /*@__PURE__*/ createScopedWaapiAnimate(); + + /** + * A time in milliseconds, beyond which we consider the scroll velocity to be 0. + */ + const maxElapsed = 50; + const createAxisInfo = () => ({ + current: 0, + offset: [], + progress: 0, + scrollLength: 0, + targetOffset: 0, + targetLength: 0, + containerLength: 0, + velocity: 0, + }); + const createScrollInfo = () => ({ + time: 0, + x: createAxisInfo(), + y: createAxisInfo(), + }); + const keys = { + x: { + length: "Width", + position: "Left", + }, + y: { + length: "Height", + position: "Top", + }, + }; + function updateAxisInfo(element, axisName, info, time) { + const axis = info[axisName]; + const { length, position } = keys[axisName]; + const prev = axis.current; + const prevTime = info.time; + axis.current = element[`scroll${position}`]; + axis.scrollLength = element[`scroll${length}`] - element[`client${length}`]; + axis.offset.length = 0; + axis.offset[0] = 0; + axis.offset[1] = axis.scrollLength; + axis.progress = progress(0, axis.scrollLength, axis.current); + const elapsed = time - prevTime; + axis.velocity = + elapsed > maxElapsed + ? 0 + : velocityPerSecond(axis.current - prev, elapsed); + } + function updateScrollInfo(element, info, time) { + updateAxisInfo(element, "x", info, time); + updateAxisInfo(element, "y", info, time); + info.time = time; + } + + function calcInset(element, container) { + const inset = { x: 0, y: 0 }; + let current = element; + while (current && current !== container) { + if (isHTMLElement(current)) { + inset.x += current.offsetLeft; + inset.y += current.offsetTop; + current = current.offsetParent; + } + else if (current.tagName === "svg") { + /** + * This isn't an ideal approach to measuring the offset of tags. + * It would be preferable, given they behave like HTMLElements in most ways + * to use offsetLeft/Top. But these don't exist on . Likewise we + * can't use .getBBox() like most SVG elements as these provide the offset + * relative to the SVG itself, which for is usually 0x0. + */ + const svgBoundingBox = current.getBoundingClientRect(); + current = current.parentElement; + const parentBoundingBox = current.getBoundingClientRect(); + inset.x += svgBoundingBox.left - parentBoundingBox.left; + inset.y += svgBoundingBox.top - parentBoundingBox.top; + } + else if (current instanceof SVGGraphicsElement) { + const { x, y } = current.getBBox(); + inset.x += x; + inset.y += y; + let svg = null; + let parent = current.parentNode; + while (!svg) { + if (parent.tagName === "svg") { + svg = parent; + } + parent = current.parentNode; + } + current = svg; + } + else { + break; + } + } + return inset; + } + + const namedEdges = { + start: 0, + center: 0.5, + end: 1, + }; + function resolveEdge(edge, length, inset = 0) { + let delta = 0; + /** + * If we have this edge defined as a preset, replace the definition + * with the numerical value. + */ + if (edge in namedEdges) { + edge = namedEdges[edge]; + } + /** + * Handle unit values + */ + if (typeof edge === "string") { + const asNumber = parseFloat(edge); + if (edge.endsWith("px")) { + delta = asNumber; + } + else if (edge.endsWith("%")) { + edge = asNumber / 100; + } + else if (edge.endsWith("vw")) { + delta = (asNumber / 100) * document.documentElement.clientWidth; + } + else if (edge.endsWith("vh")) { + delta = (asNumber / 100) * document.documentElement.clientHeight; + } + else { + edge = asNumber; + } + } + /** + * If the edge is defined as a number, handle as a progress value. + */ + if (typeof edge === "number") { + delta = length * edge; + } + return inset + delta; + } + + const defaultOffset = [0, 0]; + function resolveOffset(offset, containerLength, targetLength, targetInset) { + let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset; + let targetPoint = 0; + let containerPoint = 0; + if (typeof offset === "number") { + /** + * If we're provided offset: [0, 0.5, 1] then each number x should become + * [x, x], so we default to the behaviour of mapping 0 => 0 of both target + * and container etc. + */ + offsetDefinition = [offset, offset]; + } + else if (typeof offset === "string") { + offset = offset.trim(); + if (offset.includes(" ")) { + offsetDefinition = offset.split(" "); + } + else { + /** + * If we're provided a definition like "100px" then we want to apply + * that only to the top of the target point, leaving the container at 0. + * Whereas a named offset like "end" should be applied to both. + */ + offsetDefinition = [offset, namedEdges[offset] ? offset : `0`]; + } + } + targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset); + containerPoint = resolveEdge(offsetDefinition[1], containerLength); + return targetPoint - containerPoint; + } + + const ScrollOffset = { + Enter: [ + [0, 1], + [1, 1], + ], + Exit: [ + [0, 0], + [1, 0], + ], + Any: [ + [1, 0], + [0, 1], + ], + All: [ + [0, 0], + [1, 1], + ], + }; + + const point = { x: 0, y: 0 }; + function getTargetSize(target) { + return "getBBox" in target && target.tagName !== "svg" + ? target.getBBox() + : { width: target.clientWidth, height: target.clientHeight }; + } + function resolveOffsets(container, info, options) { + const { offset: offsetDefinition = ScrollOffset.All } = options; + const { target = container, axis = "y" } = options; + const lengthLabel = axis === "y" ? "height" : "width"; + const inset = target !== container ? calcInset(target, container) : point; + /** + * Measure the target and container. If they're the same thing then we + * use the container's scrollWidth/Height as the target, from there + * all other calculations can remain the same. + */ + const targetSize = target === container + ? { width: container.scrollWidth, height: container.scrollHeight } + : getTargetSize(target); + const containerSize = { + width: container.clientWidth, + height: container.clientHeight, + }; + /** + * Reset the length of the resolved offset array rather than creating a new one. + * TODO: More reusable data structures for targetSize/containerSize would also be good. + */ + info[axis].offset.length = 0; + /** + * Populate the offset array by resolving the user's offset definition into + * a list of pixel scroll offets. + */ + let hasChanged = !info[axis].interpolate; + const numOffsets = offsetDefinition.length; + for (let i = 0; i < numOffsets; i++) { + const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]); + if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) { + hasChanged = true; + } + info[axis].offset[i] = offset; + } + /** + * If the pixel scroll offsets have changed, create a new interpolator function + * to map scroll value into a progress. + */ + if (hasChanged) { + info[axis].interpolate = interpolate(info[axis].offset, defaultOffset$1(offsetDefinition), { clamp: false }); + info[axis].interpolatorOffsets = [...info[axis].offset]; + } + info[axis].progress = clamp(0, 1, info[axis].interpolate(info[axis].current)); + } + + function measure(container, target = container, info) { + /** + * Find inset of target within scrollable container + */ + info.x.targetOffset = 0; + info.y.targetOffset = 0; + if (target !== container) { + let node = target; + while (node && node !== container) { + info.x.targetOffset += node.offsetLeft; + info.y.targetOffset += node.offsetTop; + node = node.offsetParent; + } + } + info.x.targetLength = + target === container ? target.scrollWidth : target.clientWidth; + info.y.targetLength = + target === container ? target.scrollHeight : target.clientHeight; + info.x.containerLength = container.clientWidth; + info.y.containerLength = container.clientHeight; + /** + * In development mode ensure scroll containers aren't position: static as this makes + * it difficult to measure their relative positions. + */ + { + if (container && target && target !== container) { + warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly."); + } + } + } + function createOnScrollHandler(element, onScroll, info, options = {}) { + return { + measure: (time) => { + measure(element, options.target, info); + updateScrollInfo(element, info, time); + if (options.offset || options.target) { + resolveOffsets(element, info, options); + } + }, + notify: () => onScroll(info), + }; + } + + const scrollListeners = new WeakMap(); + const resizeListeners = new WeakMap(); + const onScrollHandlers = new WeakMap(); + const getEventTarget = (element) => element === document.scrollingElement ? window : element; + function scrollInfo(onScroll, { container = document.scrollingElement, ...options } = {}) { + if (!container) + return noop; + let containerHandlers = onScrollHandlers.get(container); + /** + * Get the onScroll handlers for this container. + * If one isn't found, create a new one. + */ + if (!containerHandlers) { + containerHandlers = new Set(); + onScrollHandlers.set(container, containerHandlers); + } + /** + * Create a new onScroll handler for the provided callback. + */ + const info = createScrollInfo(); + const containerHandler = createOnScrollHandler(container, onScroll, info, options); + containerHandlers.add(containerHandler); + /** + * Check if there's a scroll event listener for this container. + * If not, create one. + */ + if (!scrollListeners.has(container)) { + const measureAll = () => { + for (const handler of containerHandlers) { + handler.measure(frameData.timestamp); + } + frame.preUpdate(notifyAll); + }; + const notifyAll = () => { + for (const handler of containerHandlers) { + handler.notify(); + } + }; + const listener = () => frame.read(measureAll); + scrollListeners.set(container, listener); + const target = getEventTarget(container); + window.addEventListener("resize", listener, { passive: true }); + if (container !== document.documentElement) { + resizeListeners.set(container, resize(container, listener)); + } + target.addEventListener("scroll", listener, { passive: true }); + listener(); + } + const listener = scrollListeners.get(container); + frame.read(listener, false, true); + return () => { + cancelFrame(listener); + /** + * Check if we even have any handlers for this container. + */ + const currentHandlers = onScrollHandlers.get(container); + if (!currentHandlers) + return; + currentHandlers.delete(containerHandler); + if (currentHandlers.size) + return; + /** + * If no more handlers, remove the scroll listener too. + */ + const scrollListener = scrollListeners.get(container); + scrollListeners.delete(container); + if (scrollListener) { + getEventTarget(container).removeEventListener("scroll", scrollListener); + resizeListeners.get(container)?.(); + window.removeEventListener("resize", scrollListener); + } + }; + } + + const timelineCache = new Map(); + function scrollTimelineFallback(options) { + const currentTime = { value: 0 }; + const cancel = scrollInfo((info) => { + currentTime.value = info[options.axis].progress * 100; + }, options); + return { currentTime, cancel }; + } + function getTimeline({ source, container, ...options }) { + const { axis } = options; + if (source) + container = source; + const containerCache = timelineCache.get(container) ?? new Map(); + timelineCache.set(container, containerCache); + const targetKey = options.target ?? "self"; + const targetCache = containerCache.get(targetKey) ?? {}; + const axisKey = axis + (options.offset ?? []).join(","); + if (!targetCache[axisKey]) { + targetCache[axisKey] = + !options.target && supportsScrollTimeline() + ? new ScrollTimeline({ source: container, axis }) + : scrollTimelineFallback({ container, ...options }); + } + return targetCache[axisKey]; + } + + function attachToAnimation(animation, options) { + const timeline = getTimeline(options); + return animation.attachTimeline({ + timeline: options.target ? undefined : timeline, + observe: (valueAnimation) => { + valueAnimation.pause(); + return observeTimeline((progress) => { + valueAnimation.time = valueAnimation.duration * progress; + }, timeline); + }, + }); + } + + /** + * If the onScroll function has two arguments, it's expecting + * more specific information about the scroll from scrollInfo. + */ + function isOnScrollWithInfo(onScroll) { + return onScroll.length === 2; + } + function attachToFunction(onScroll, options) { + if (isOnScrollWithInfo(onScroll)) { + return scrollInfo((info) => { + onScroll(info[options.axis].progress, info); + }, options); + } + else { + return observeTimeline(onScroll, getTimeline(options)); + } + } + + function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) { + if (!container) + return noop; + const optionsWithDefaults = { axis, container, ...options }; + return typeof onScroll === "function" + ? attachToFunction(onScroll, optionsWithDefaults) + : attachToAnimation(onScroll, optionsWithDefaults); + } + + const thresholds = { + some: 0, + all: 1, + }; + function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) { + const elements = resolveElements(elementOrSelector); + const activeIntersections = new WeakMap(); + const onIntersectionChange = (entries) => { + entries.forEach((entry) => { + const onEnd = activeIntersections.get(entry.target); + /** + * If there's no change to the intersection, we don't need to + * do anything here. + */ + if (entry.isIntersecting === Boolean(onEnd)) + return; + if (entry.isIntersecting) { + const newOnEnd = onStart(entry.target, entry); + if (typeof newOnEnd === "function") { + activeIntersections.set(entry.target, newOnEnd); + } + else { + observer.unobserve(entry.target); + } + } + else if (typeof onEnd === "function") { + onEnd(entry); + activeIntersections.delete(entry.target); + } + }); + }; + const observer = new IntersectionObserver(onIntersectionChange, { + root, + rootMargin, + threshold: typeof amount === "number" ? amount : thresholds[amount], + }); + elements.forEach((element) => observer.observe(element)); + return () => observer.disconnect(); + } + + const createMinimalMotionComponent = + /*@__PURE__*/ createMotionComponentFactory(); + + const m = /*@__PURE__*/ createDOMMotionComponentProxy(createMinimalMotionComponent); + + function useUnmountEffect(callback) { + return React$1.useEffect(() => () => callback(), []); + } + + /** + * @public + */ + const domAnimation = { + renderer: createDomVisualElement, + ...animations, + ...gestureAnimations, + }; + + /** + * @public + */ + const domMax = { + ...domAnimation, + ...drag, + ...layout, + }; + + /** + * @public + */ + const domMin = { + renderer: createDomVisualElement, + ...animations, + }; + + function useMotionValueEvent(value, event, callback) { + /** + * useInsertionEffect will create subscriptions before any other + * effects will run. Effects run upwards through the tree so it + * can be that binding a useLayoutEffect higher up the tree can + * miss changes from lower down the tree. + */ + React$1.useInsertionEffect(() => value.on(event, callback), [value, event, callback]); + } + + function refWarning(name, ref) { + exports.warning(Boolean(!ref || ref.current), `You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`); + } + const createScrollMotionValues = () => ({ + scrollX: motionValue(0), + scrollY: motionValue(0), + scrollXProgress: motionValue(0), + scrollYProgress: motionValue(0), + }); + function useScroll({ container, target, layoutEffect = true, ...options } = {}) { + const values = useConstant(createScrollMotionValues); + const useLifecycleEffect = layoutEffect + ? useIsomorphicLayoutEffect + : React$1.useEffect; + useLifecycleEffect(() => { + refWarning("target", target); + refWarning("container", container); + return scroll((_progress, { x, y, }) => { + values.scrollX.set(x.current); + values.scrollXProgress.set(x.progress); + values.scrollY.set(y.current); + values.scrollYProgress.set(y.progress); + }, { + ...options, + container: container?.current || undefined, + target: target?.current || undefined, + }); + }, [container, target, JSON.stringify(options.offset)]); + return values; + } + + /** + * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref }) + */ + function useElementScroll(ref) { + { + warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref })."); + } + return useScroll({ container: ref }); + } + + /** + * @deprecated useViewportScroll is deprecated. Convert to useScroll() + */ + function useViewportScroll() { + { + warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll()."); + } + return useScroll(); + } + + /** + * Combine multiple motion values into a new one using a string template literal. + * + * ```jsx + * import { + * motion, + * useSpring, + * useMotionValue, + * useMotionTemplate + * } from "framer-motion" + * + * function Component() { + * const shadowX = useSpring(0) + * const shadowY = useMotionValue(0) + * const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))` + * + * return + * } + * ``` + * + * @public + */ + function useMotionTemplate(fragments, ...values) { + /** + * Create a function that will build a string from the latest motion values. + */ + const numFragments = fragments.length; + function buildValue() { + let output = ``; + for (let i = 0; i < numFragments; i++) { + output += fragments[i]; + const value = values[i]; + if (value) { + output += isMotionValue(value) ? value.get() : value; + } + } + return output; + } + return useCombineMotionValues(values.filter(isMotionValue), buildValue); + } + + function useSpring(source, options = {}) { + const { isStatic } = React$1.useContext(MotionConfigContext); + const getFromSource = () => (isMotionValue(source) ? source.get() : source); + // isStatic will never change, allowing early hooks return + if (isStatic) { + return useTransform(getFromSource); + } + const value = useMotionValue(getFromSource()); + React$1.useInsertionEffect(() => { + return attachSpring(value, source, options); + }, [value, JSON.stringify(options)]); + return value; + } + + function useAnimationFrame(callback) { + const initialTimestamp = React$1.useRef(0); + const { isStatic } = React$1.useContext(MotionConfigContext); + React$1.useEffect(() => { + if (isStatic) + return; + const provideTimeSinceStart = ({ timestamp, delta }) => { + if (!initialTimestamp.current) + initialTimestamp.current = timestamp; + callback(timestamp - initialTimestamp.current, delta); + }; + frame.update(provideTimeSinceStart, true); + return () => cancelFrame(provideTimeSinceStart); + }, [callback]); + } + + function useTime() { + const time = useMotionValue(0); + useAnimationFrame((t) => time.set(t)); + return time; + } + + /** + * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes. + * + * ```javascript + * const x = useMotionValue(0) + * const xVelocity = useVelocity(x) + * const xAcceleration = useVelocity(xVelocity) + * ``` + * + * @public + */ + function useVelocity(value) { + const velocity = useMotionValue(value.getVelocity()); + const updateVelocity = () => { + const latest = value.getVelocity(); + velocity.set(latest); + /** + * If we still have velocity, schedule an update for the next frame + * to keep checking until it is zero. + */ + if (latest) + frame.update(updateVelocity); + }; + useMotionValueEvent(value, "change", () => { + // Schedule an update to this value at the end of the current frame. + frame.update(updateVelocity, false, true); + }); + return velocity; + } + + class WillChangeMotionValue extends MotionValue { + constructor() { + super(...arguments); + this.isEnabled = false; + } + add(name) { + if (transformProps.has(name) || acceleratedValues.has(name)) { + this.isEnabled = true; + this.update(); + } + } + update() { + this.set(this.isEnabled ? "transform" : "auto"); + } + } + + function useWillChange() { + return useConstant(() => new WillChangeMotionValue("auto")); + } + + /** + * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting. + * + * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing + * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. + * + * It will actively respond to changes and re-render your components with the latest setting. + * + * ```jsx + * export function Sidebar({ isOpen }) { + * const shouldReduceMotion = useReducedMotion() + * const closedX = shouldReduceMotion ? 0 : "-100%" + * + * return ( + * + * ) + * } + * ``` + * + * @return boolean + * + * @public + */ + function useReducedMotion() { + /** + * Lazy initialisation of prefersReducedMotion + */ + !hasReducedMotionListener.current && initPrefersReducedMotion(); + const [shouldReduceMotion] = React$1.useState(prefersReducedMotion.current); + { + warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected."); + } + /** + * TODO See if people miss automatically updating shouldReduceMotion setting + */ + return shouldReduceMotion; + } + + function useReducedMotionConfig() { + const reducedMotionPreference = useReducedMotion(); + const { reducedMotion } = React$1.useContext(MotionConfigContext); + if (reducedMotion === "never") { + return false; + } + else if (reducedMotion === "always") { + return true; + } + else { + return reducedMotionPreference; + } + } + + function stopAnimation(visualElement) { + visualElement.values.forEach((value) => value.stop()); + } + function setVariants(visualElement, variantLabels) { + const reversedLabels = [...variantLabels].reverse(); + reversedLabels.forEach((key) => { + const variant = visualElement.getVariant(key); + variant && setTarget(visualElement, variant); + if (visualElement.variantChildren) { + visualElement.variantChildren.forEach((child) => { + setVariants(child, variantLabels); + }); + } + }); + } + function setValues(visualElement, definition) { + if (Array.isArray(definition)) { + return setVariants(visualElement, definition); + } + else if (typeof definition === "string") { + return setVariants(visualElement, [definition]); + } + else { + setTarget(visualElement, definition); + } + } + /** + * @public + */ + function animationControls() { + /** + * Track whether the host component has mounted. + */ + let hasMounted = false; + /** + * A collection of linked component animation controls. + */ + const subscribers = new Set(); + const controls = { + subscribe(visualElement) { + subscribers.add(visualElement); + return () => void subscribers.delete(visualElement); + }, + start(definition, transitionOverride) { + exports.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook."); + const animations = []; + subscribers.forEach((visualElement) => { + animations.push(animateVisualElement(visualElement, definition, { + transitionOverride, + })); + }); + return Promise.all(animations); + }, + set(definition) { + exports.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook."); + return subscribers.forEach((visualElement) => { + setValues(visualElement, definition); + }); + }, + stop() { + subscribers.forEach((visualElement) => { + stopAnimation(visualElement); + }); + }, + mount() { + hasMounted = true; + return () => { + hasMounted = false; + controls.stop(); + }; + }, + }; + return controls; + } + + function useAnimate() { + const scope = useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = useConstant(() => createScopedAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + scope.animations.length = 0; + }); + return [scope, animate]; + } + + function useAnimateMini() { + const scope = useConstant(() => ({ + current: null, // Will be hydrated by React + animations: [], + })); + const animate = useConstant(() => createScopedWaapiAnimate(scope)); + useUnmountEffect(() => { + scope.animations.forEach((animation) => animation.stop()); + }); + return [scope, animate]; + } + + /** + * Creates `LegacyAnimationControls`, which can be used to manually start, stop + * and sequence animations on one or more components. + * + * The returned `LegacyAnimationControls` should be passed to the `animate` property + * of the components you want to animate. + * + * These components can then be animated with the `start` method. + * + * ```jsx + * import * as React from 'react' + * import { motion, useAnimation } from 'framer-motion' + * + * export function MyComponent(props) { + * const controls = useAnimation() + * + * controls.start({ + * x: 100, + * transition: { duration: 0.5 }, + * }) + * + * return + * } + * ``` + * + * @returns Animation controller with `start` and `stop` methods + * + * @public + */ + function useAnimationControls() { + const controls = useConstant(animationControls); + useIsomorphicLayoutEffect(controls.mount, []); + return controls; + } + const useAnimation = useAnimationControls; + + function usePresenceData() { + const context = React$1.useContext(PresenceContext); + return context ? context.custom : undefined; + } + + /** + * Attaches an event listener directly to the provided DOM element. + * + * Bypassing React's event system can be desirable, for instance when attaching non-passive + * event handlers. + * + * ```jsx + * const ref = useRef(null) + * + * useDomEvent(ref, 'wheel', onWheel, { passive: false }) + * + * return
+ * ``` + * + * @param ref - React.RefObject that's been provided to the element you want to bind the listener to. + * @param eventName - Name of the event you want listen for. + * @param handler - Function to fire when receiving the event. + * @param options - Options to pass to `Event.addEventListener`. + * + * @public + */ + function useDomEvent(ref, eventName, handler, options) { + React$1.useEffect(() => { + const element = ref.current; + if (handler && element) { + return addDomEvent(element, eventName, handler, options); + } + }, [ref, eventName, handler, options]); + } + + /** + * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ + class DragControls { + constructor() { + this.componentControls = new Set(); + } + /** + * Subscribe a component's internal `VisualElementDragControls` to the user-facing API. + * + * @internal + */ + subscribe(controls) { + this.componentControls.add(controls); + return () => this.componentControls.delete(controls); + } + /** + * Start a drag gesture on every `motion` component that has this set of drag controls + * passed into it via the `dragControls` prop. + * + * ```jsx + * dragControls.start(e, { + * snapToCursor: true + * }) + * ``` + * + * @param event - PointerEvent + * @param options - Options + * + * @public + */ + start(event, options) { + this.componentControls.forEach((controls) => { + controls.start(event.nativeEvent || event, options); + }); + } + /** + * Cancels a drag gesture. + * + * ```jsx + * dragControls.cancel() + * ``` + * + * @public + */ + cancel() { + this.componentControls.forEach((controls) => { + controls.cancel(); + }); + } + /** + * Stops a drag gesture. + * + * ```jsx + * dragControls.stop() + * ``` + * + * @public + */ + stop() { + this.componentControls.forEach((controls) => { + controls.stop(); + }); + } + } + const createDragControls = () => new DragControls(); + /** + * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop + * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we + * might want to initiate that dragging from a different component than the draggable one. + * + * By creating a `dragControls` using the `useDragControls` hook, we can pass this into + * the draggable component's `dragControls` prop. It exposes a `start` method + * that can start dragging from pointer events on other components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ + function useDragControls() { + return useConstant(createDragControls); + } + + /** + * Checks if a component is a `motion` component. + */ + function isMotionComponent(component) { + return (component !== null && + typeof component === "object" && + motionComponentSymbol in component); + } + + /** + * Unwraps a `motion` component and returns either a string for `motion.div` or + * the React component for `motion(Component)`. + * + * If the component is not a `motion` component it returns undefined. + */ + function unwrapMotionComponent(component) { + if (isMotionComponent(component)) { + return component[motionComponentSymbol]; + } + return undefined; + } + + function useInstantLayoutTransition() { + return startTransition; + } + function startTransition(callback) { + if (!rootProjectionNode.current) + return; + rootProjectionNode.current.isUpdating = false; + rootProjectionNode.current.blockUpdate(); + callback && callback(); + } + + function useResetProjection() { + const reset = React$1.useCallback(() => { + const root = rootProjectionNode.current; + if (!root) + return; + root.resetTree(); + }, []); + return reset; + } + + /** + * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments. + * + * An index value can be passed to the returned `cycle` function to cycle to a specific index. + * + * ```jsx + * import * as React from "react" + * import { motion, useCycle } from "framer-motion" + * + * export const MyComponent = () => { + * const [x, cycleX] = useCycle(0, 50, 100) + * + * return ( + * cycleX()} + * /> + * ) + * } + * ``` + * + * @param items - items to cycle through + * @returns [currentState, cycleState] + * + * @public + */ + function useCycle(...items) { + const index = React$1.useRef(0); + const [item, setItem] = React$1.useState(items[index.current]); + const runCycle = React$1.useCallback((next) => { + index.current = + typeof next !== "number" + ? wrap(0, items.length, index.current + 1) + : next; + setItem(items[index.current]); + }, + // The array will change on each call, but by putting items.length at + // the front of this array, we guarantee the dependency comparison will match up + // eslint-disable-next-line react-hooks/exhaustive-deps + [items.length, ...items]); + return [item, runCycle]; + } + + function useInView(ref, { root, margin, amount, once = false, initial = false, } = {}) { + const [isInView, setInView] = React$1.useState(initial); + React$1.useEffect(() => { + if (!ref.current || (once && isInView)) + return; + const onEnter = () => { + setInView(true); + return once ? undefined : () => setInView(false); + }; + const options = { + root: (root && root.current) || undefined, + margin, + amount, + }; + return inView(ref.current, onEnter, options); + }, [root, ref, margin, once, amount]); + return isInView; + } + + function useInstantTransition() { + const [forceUpdate, forcedRenderCount] = useForceUpdate(); + const startInstantLayoutTransition = useInstantLayoutTransition(); + const unlockOnFrameRef = React$1.useRef(-1); + React$1.useEffect(() => { + /** + * Unblock after two animation frames, otherwise this will unblock too soon. + */ + frame.postRender(() => frame.postRender(() => { + /** + * If the callback has been called again after the effect + * triggered this 2 frame delay, don't unblock animations. This + * prevents the previous effect from unblocking the current + * instant transition too soon. This becomes more likely when + * used in conjunction with React.startTransition(). + */ + if (forcedRenderCount !== unlockOnFrameRef.current) + return; + MotionGlobalConfig.instantAnimations = false; + })); + }, [forcedRenderCount]); + return (callback) => { + startInstantLayoutTransition(() => { + MotionGlobalConfig.instantAnimations = true; + forceUpdate(); + callback(); + unlockOnFrameRef.current = forcedRenderCount + 1; + }); + }; + } + function disableInstantTransitions() { + MotionGlobalConfig.instantAnimations = false; + } + + function usePageInView() { + const [isInView, setIsInView] = React$1.useState(true); + React$1.useEffect(() => { + const handleVisibilityChange = () => setIsInView(!document.hidden); + if (document.hidden) { + handleVisibilityChange(); + } + document.addEventListener("visibilitychange", handleVisibilityChange); + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, []); + return isInView; + } + + const appearAnimationStore = new Map(); + const appearComplete = new Map(); + + const appearStoreId = (elementId, valueName) => { + const key = transformProps.has(valueName) ? "transform" : valueName; + return `${elementId}: ${key}`; + }; + + function handoffOptimizedAppearAnimation(elementId, valueName, frame) { + const storeId = appearStoreId(elementId, valueName); + const optimisedAnimation = appearAnimationStore.get(storeId); + if (!optimisedAnimation) { + return null; + } + const { animation, startTime } = optimisedAnimation; + function cancelAnimation() { + window.MotionCancelOptimisedAnimation?.(elementId, valueName, frame); + } + /** + * We can cancel the animation once it's finished now that we've synced + * with Motion. + * + * Prefer onfinish over finished as onfinish is backwards compatible with + * older browsers. + */ + animation.onfinish = cancelAnimation; + if (startTime === null || window.MotionHandoffIsComplete?.(elementId)) { + /** + * If the startTime is null, this animation is the Paint Ready detection animation + * and we can cancel it immediately without handoff. + * + * Or if we've already handed off the animation then we're now interrupting it. + * In which case we need to cancel it. + */ + cancelAnimation(); + return null; + } + else { + return startTime; + } + } + + /** + * A single time to use across all animations to manually set startTime + * and ensure they're all in sync. + */ + let startFrameTime; + /** + * A dummy animation to detect when Chrome is ready to start + * painting the page and hold off from triggering the real animation + * until then. We only need one animation to detect paint ready. + * + * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850 + */ + let readyAnimation; + /** + * Keep track of animations that were suspended vs cancelled so we + * can easily resume them when we're done measuring layout. + */ + const suspendedAnimations = new Set(); + function resumeSuspendedAnimations() { + suspendedAnimations.forEach((data) => { + data.animation.play(); + data.animation.startTime = data.startTime; + }); + suspendedAnimations.clear(); + } + function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) { + // Prevent optimised appear animations if Motion has already started animating. + if (window.MotionIsMounted) { + return; + } + const id = element.dataset[optimizedAppearDataId]; + if (!id) + return; + window.MotionHandoffAnimation = handoffOptimizedAppearAnimation; + const storeId = appearStoreId(id, name); + if (!readyAnimation) { + readyAnimation = startWaapiAnimation(element, name, [keyframes[0], keyframes[0]], + /** + * 10 secs is basically just a super-safe duration to give Chrome + * long enough to get the animation ready. + */ + { duration: 10000, ease: "linear" }); + appearAnimationStore.set(storeId, { + animation: readyAnimation, + startTime: null, + }); + /** + * If there's no readyAnimation then there's been no instantiation + * of handoff animations. + */ + window.MotionHandoffAnimation = handoffOptimizedAppearAnimation; + window.MotionHasOptimisedAnimation = (elementId, valueName) => { + if (!elementId) + return false; + /** + * Keep a map of elementIds that have started animating. We check + * via ID instead of Element because of hydration errors and + * pre-hydration checks. We also actively record IDs as they start + * animating rather than simply checking for data-appear-id as + * this attrbute might be present but not lead to an animation, for + * instance if the element's appear animation is on a different + * breakpoint. + */ + if (!valueName) { + return appearComplete.has(elementId); + } + const animationId = appearStoreId(elementId, valueName); + return Boolean(appearAnimationStore.get(animationId)); + }; + window.MotionHandoffMarkAsComplete = (elementId) => { + if (appearComplete.has(elementId)) { + appearComplete.set(elementId, true); + } + }; + window.MotionHandoffIsComplete = (elementId) => { + return appearComplete.get(elementId) === true; + }; + /** + * We only need to cancel transform animations as + * they're the ones that will interfere with the + * layout animation measurements. + */ + window.MotionCancelOptimisedAnimation = (elementId, valueName, frame, canResume) => { + const animationId = appearStoreId(elementId, valueName); + const data = appearAnimationStore.get(animationId); + if (!data) + return; + if (frame && canResume === undefined) { + /** + * Wait until the end of the subsequent frame to cancel the animation + * to ensure we don't remove the animation before the main thread has + * had a chance to resolve keyframes and render. + */ + frame.postRender(() => { + frame.postRender(() => { + data.animation.cancel(); + }); + }); + } + else { + data.animation.cancel(); + } + if (frame && canResume) { + suspendedAnimations.add(data); + frame.render(resumeSuspendedAnimations); + } + else { + appearAnimationStore.delete(animationId); + /** + * If there are no more animations left, we can remove the cancel function. + * This will let us know when we can stop checking for conflicting layout animations. + */ + if (!appearAnimationStore.size) { + window.MotionCancelOptimisedAnimation = undefined; + } + } + }; + window.MotionCheckAppearSync = (visualElement, valueName, value) => { + const appearId = getOptimisedAppearId(visualElement); + if (!appearId) + return; + const valueIsOptimised = window.MotionHasOptimisedAnimation?.(appearId, valueName); + const externalAnimationValue = visualElement.props.values?.[valueName]; + if (!valueIsOptimised || !externalAnimationValue) + return; + const removeSyncCheck = value.on("change", (latestValue) => { + if (externalAnimationValue.get() !== latestValue) { + window.MotionCancelOptimisedAnimation?.(appearId, valueName); + removeSyncCheck(); + } + }); + return removeSyncCheck; + }; + } + const startAnimation = () => { + readyAnimation.cancel(); + const appearAnimation = startWaapiAnimation(element, name, keyframes, options); + /** + * Record the time of the first started animation. We call performance.now() once + * here and once in handoff to ensure we're getting + * close to a frame-locked time. This keeps all animations in sync. + */ + if (startFrameTime === undefined) { + startFrameTime = performance.now(); + } + appearAnimation.startTime = startFrameTime; + appearAnimationStore.set(storeId, { + animation: appearAnimation, + startTime: startFrameTime, + }); + if (onReady) + onReady(appearAnimation); + }; + appearComplete.set(id, false); + if (readyAnimation.ready) { + readyAnimation.ready.then(startAnimation).catch(noop); + } + else { + startAnimation(); + } + } + + const createObject = () => ({}); + class StateVisualElement extends VisualElement { + constructor() { + super(...arguments); + this.measureInstanceViewportBox = createBox; + } + build() { } + resetTransform() { } + restoreTransform() { } + removeValueFromRenderState() { } + renderInstance() { } + scrapeMotionValuesFromProps() { + return createObject(); + } + getBaseTargetFromProps() { + return undefined; + } + readValueFromInstance(_state, key, options) { + return options.initialState[key] || 0; + } + sortInstanceNodePosition() { + return 0; + } + } + const useVisualState = makeUseVisualState({ + scrapeMotionValuesFromProps: createObject, + createRenderState: createObject, + }); + /** + * This is not an officially supported API and may be removed + * on any version. + */ + function useAnimatedState(initialState) { + const [animationState, setAnimationState] = React$1.useState(initialState); + const visualState = useVisualState({}, false); + const element = useConstant(() => { + return new StateVisualElement({ + props: { + onUpdate: (v) => { + setAnimationState({ ...v }); + }, + }, + visualState, + presenceContext: null, + }, { initialState }); + }); + React$1.useLayoutEffect(() => { + element.mount({}); + return () => element.unmount(); + }, [element]); + const startAnimation = useConstant(() => (animationDefinition) => { + return animateVisualElement(element, animationDefinition); + }); + return [animationState, startAnimation]; + } + + let id = 0; + const AnimateSharedLayout = ({ children }) => { + React__namespace.useEffect(() => { + exports.invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations"); + }, []); + return (jsx(LayoutGroup, { id: useConstant(() => `asl-${id++}`), children: children })); + }; + + // Keep things reasonable and avoid scale: Infinity. In practise we might need + // to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1] + // to simply hide content at unreasonable scales. + const maxScale = 100000; + const invertScale = (scale) => scale > 0.001 ? 1 / scale : maxScale; + let hasWarned = false; + /** + * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse + * of their respective parent scales. + * + * This is useful for undoing the distortion of content when scaling a parent component. + * + * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent. + * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output + * of those instead. + * + * ```jsx + * const MyComponent = () => { + * const { scaleX, scaleY } = useInvertedScale() + * return + * } + * ``` + * + * @deprecated + */ + function useInvertedScale(scale) { + let parentScaleX = useMotionValue(1); + let parentScaleY = useMotionValue(1); + const { visualElement } = React$1.useContext(MotionContext); + exports.invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component."); + exports.warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead."); + hasWarned = true; + if (scale) { + parentScaleX = scale.scaleX || parentScaleX; + parentScaleY = scale.scaleY || parentScaleY; + } + else if (visualElement) { + parentScaleX = visualElement.getValue("scaleX", 1); + parentScaleY = visualElement.getValue("scaleY", 1); + } + const scaleX = useTransform(parentScaleX, invertScale); + const scaleY = useTransform(parentScaleY, invertScale); + return { scaleX, scaleY }; + } + + exports.AnimatePresence = AnimatePresence; + exports.AnimateSharedLayout = AnimateSharedLayout; + exports.AsyncMotionValueAnimation = AsyncMotionValueAnimation; + exports.DOMKeyframesResolver = DOMKeyframesResolver; + exports.DeprecatedLayoutGroupContext = DeprecatedLayoutGroupContext; + exports.DragControls = DragControls; + exports.FlatTree = FlatTree; + exports.GroupAnimation = GroupAnimation; + exports.GroupAnimationWithThen = GroupAnimationWithThen; + exports.JSAnimation = JSAnimation; + exports.KeyframeResolver = KeyframeResolver; + exports.LayoutGroup = LayoutGroup; + exports.LayoutGroupContext = LayoutGroupContext; + exports.LazyMotion = LazyMotion; + exports.MotionConfig = MotionConfig; + exports.MotionConfigContext = MotionConfigContext; + exports.MotionContext = MotionContext; + exports.MotionGlobalConfig = MotionGlobalConfig; + exports.MotionValue = MotionValue; + exports.NativeAnimation = NativeAnimation; + exports.NativeAnimationExtended = NativeAnimationExtended; + exports.NativeAnimationWrapper = NativeAnimationWrapper; + exports.PresenceContext = PresenceContext; + exports.Reorder = namespace; + exports.SubscriptionManager = SubscriptionManager; + exports.SwitchLayoutGroupContext = SwitchLayoutGroupContext; + exports.ViewTransitionBuilder = ViewTransitionBuilder; + exports.VisualElement = VisualElement; + exports.WillChangeMotionValue = WillChangeMotionValue; + exports.acceleratedValues = acceleratedValues; + exports.activeAnimations = activeAnimations; + exports.addAttrValue = addAttrValue; + exports.addPointerEvent = addPointerEvent; + exports.addPointerInfo = addPointerInfo; + exports.addScaleCorrector = addScaleCorrector; + exports.addStyleValue = addStyleValue; + exports.addUniqueItem = addUniqueItem; + exports.alpha = alpha; + exports.analyseComplexValue = analyseComplexValue; + exports.animate = animate; + exports.animateMini = animateMini; + exports.animateValue = animateValue; + exports.animateView = animateView; + exports.animateVisualElement = animateVisualElement; + exports.animationControls = animationControls; + exports.animationMapKey = animationMapKey; + exports.animations = animations; + exports.anticipate = anticipate; + exports.applyPxDefaults = applyPxDefaults; + exports.attachSpring = attachSpring; + exports.attrEffect = attrEffect; + exports.backIn = backIn; + exports.backInOut = backInOut; + exports.backOut = backOut; + exports.buildTransform = buildTransform; + exports.calcGeneratorDuration = calcGeneratorDuration; + exports.calcLength = calcLength; + exports.cancelFrame = cancelFrame; + exports.cancelMicrotask = cancelMicrotask; + exports.cancelSync = cancelSync; + exports.circIn = circIn; + exports.circInOut = circInOut; + exports.circOut = circOut; + exports.clamp = clamp; + exports.collectMotionValues = collectMotionValues; + exports.color = color; + exports.complex = complex; + exports.convertOffsetToTimes = convertOffsetToTimes; + exports.createBox = createBox; + exports.createGeneratorEasing = createGeneratorEasing; + exports.createRenderBatcher = createRenderBatcher; + exports.createRendererMotionComponent = createRendererMotionComponent; + exports.createScopedAnimate = createScopedAnimate; + exports.cubicBezier = cubicBezier; + exports.cubicBezierAsString = cubicBezierAsString; + exports.defaultEasing = defaultEasing; + exports.defaultOffset = defaultOffset$1; + exports.defaultTransformValue = defaultTransformValue; + exports.defaultValueTypes = defaultValueTypes; + exports.degrees = degrees; + exports.delay = delay; + exports.dimensionValueTypes = dimensionValueTypes; + exports.disableInstantTransitions = disableInstantTransitions; + exports.distance = distance; + exports.distance2D = distance2D; + exports.domAnimation = domAnimation; + exports.domMax = domMax; + exports.domMin = domMin; + exports.easeIn = easeIn; + exports.easeInOut = easeInOut; + exports.easeOut = easeOut; + exports.easingDefinitionToFunction = easingDefinitionToFunction; + exports.fillOffset = fillOffset; + exports.fillWildcards = fillWildcards; + exports.filterProps = filterProps; + exports.findDimensionValueType = findDimensionValueType; + exports.findValueType = findValueType; + exports.flushKeyframeResolvers = flushKeyframeResolvers; + exports.frame = frame; + exports.frameData = frameData; + exports.frameSteps = frameSteps; + exports.generateLinearEasing = generateLinearEasing; + exports.getAnimatableNone = getAnimatableNone; + exports.getAnimationMap = getAnimationMap; + exports.getComputedStyle = getComputedStyle$2; + exports.getDefaultValueType = getDefaultValueType; + exports.getEasingForSegment = getEasingForSegment; + exports.getMixer = getMixer; + exports.getOriginIndex = getOriginIndex; + exports.getValueAsType = getValueAsType; + exports.getValueTransition = getValueTransition$1; + exports.getVariableValue = getVariableValue; + exports.hasWarned = hasWarned$1; + exports.hex = hex; + exports.hover = hover; + exports.hsla = hsla; + exports.hslaToRgba = hslaToRgba; + exports.inView = inView; + exports.inertia = inertia; + exports.interpolate = interpolate; + exports.invisibleValues = invisibleValues; + exports.isBezierDefinition = isBezierDefinition; + exports.isBrowser = isBrowser; + exports.isCSSVariableName = isCSSVariableName; + exports.isCSSVariableToken = isCSSVariableToken; + exports.isDragActive = isDragActive; + exports.isDragging = isDragging; + exports.isEasingArray = isEasingArray; + exports.isGenerator = isGenerator; + exports.isHTMLElement = isHTMLElement; + exports.isMotionComponent = isMotionComponent; + exports.isMotionValue = isMotionValue; + exports.isNodeOrChild = isNodeOrChild; + exports.isNumericalString = isNumericalString; + exports.isObject = isObject; + exports.isPrimaryPointer = isPrimaryPointer; + exports.isSVGElement = isSVGElement; + exports.isSVGSVGElement = isSVGSVGElement; + exports.isValidMotionProp = isValidMotionProp; + exports.isWaapiSupportedEasing = isWaapiSupportedEasing; + exports.isZeroValueString = isZeroValueString; + exports.keyframes = keyframes; + exports.m = m; + exports.makeUseVisualState = makeUseVisualState; + exports.mapEasingToNativeEasing = mapEasingToNativeEasing; + exports.mapValue = mapValue; + exports.maxGeneratorDuration = maxGeneratorDuration; + exports.memo = memo; + exports.microtask = microtask; + exports.millisecondsToSeconds = millisecondsToSeconds; + exports.mirrorEasing = mirrorEasing; + exports.mix = mix; + exports.mixArray = mixArray; + exports.mixColor = mixColor; + exports.mixComplex = mixComplex; + exports.mixImmediate = mixImmediate; + exports.mixLinearColor = mixLinearColor; + exports.mixNumber = mixNumber$1; + exports.mixObject = mixObject; + exports.mixVisibility = mixVisibility; + exports.motion = motion; + exports.motionValue = motionValue; + exports.moveItem = moveItem; + exports.noop = noop; + exports.number = number; + exports.numberValueTypes = numberValueTypes; + exports.observeTimeline = observeTimeline; + exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute; + exports.parseCSSVariable = parseCSSVariable; + exports.parseValueFromTransform = parseValueFromTransform; + exports.percent = percent; + exports.pipe = pipe; + exports.positionalKeys = positionalKeys; + exports.press = press; + exports.progress = progress; + exports.progressPercentage = progressPercentage; + exports.propEffect = propEffect; + exports.px = px; + exports.readTransformValue = readTransformValue; + exports.recordStats = recordStats; + exports.removeItem = removeItem; + exports.resize = resize; + exports.resolveElements = resolveElements; + exports.resolveMotionValue = resolveMotionValue; + exports.reverseEasing = reverseEasing; + exports.rgbUnit = rgbUnit; + exports.rgba = rgba; + exports.scale = scale; + exports.scroll = scroll; + exports.scrollInfo = scrollInfo; + exports.secondsToMilliseconds = secondsToMilliseconds; + exports.setDragLock = setDragLock; + exports.setStyle = setStyle; + exports.spring = spring; + exports.springValue = springValue; + exports.stagger = stagger; + exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation; + exports.startWaapiAnimation = startWaapiAnimation; + exports.statsBuffer = statsBuffer; + exports.steps = steps; + exports.styleEffect = styleEffect; + exports.supportedWaapiEasing = supportedWaapiEasing; + exports.supportsBrowserAnimation = supportsBrowserAnimation; + exports.supportsFlags = supportsFlags; + exports.supportsLinearEasing = supportsLinearEasing; + exports.supportsPartialKeyframes = supportsPartialKeyframes; + exports.supportsScrollTimeline = supportsScrollTimeline; + exports.svgEffect = svgEffect; + exports.sync = sync; + exports.testValueType = testValueType; + exports.time = time; + exports.transform = transform; + exports.transformPropOrder = transformPropOrder; + exports.transformProps = transformProps; + exports.transformValue = transformValue; + exports.transformValueTypes = transformValueTypes; + exports.unwrapMotionComponent = unwrapMotionComponent; + exports.useAnimate = useAnimate; + exports.useAnimateMini = useAnimateMini; + exports.useAnimation = useAnimation; + exports.useAnimationControls = useAnimationControls; + exports.useAnimationFrame = useAnimationFrame; + exports.useCycle = useCycle; + exports.useDeprecatedAnimatedState = useAnimatedState; + exports.useDeprecatedInvertedScale = useInvertedScale; + exports.useDomEvent = useDomEvent; + exports.useDragControls = useDragControls; + exports.useElementScroll = useElementScroll; + exports.useForceUpdate = useForceUpdate; + exports.useInView = useInView; + exports.useInstantLayoutTransition = useInstantLayoutTransition; + exports.useInstantTransition = useInstantTransition; + exports.useIsPresent = useIsPresent; + exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect; + exports.useMotionTemplate = useMotionTemplate; + exports.useMotionValue = useMotionValue; + exports.useMotionValueEvent = useMotionValueEvent; + exports.usePageInView = usePageInView; + exports.usePresence = usePresence; + exports.usePresenceData = usePresenceData; + exports.useReducedMotion = useReducedMotion; + exports.useReducedMotionConfig = useReducedMotionConfig; + exports.useResetProjection = useResetProjection; + exports.useScroll = useScroll; + exports.useSpring = useSpring; + exports.useTime = useTime; + exports.useTransform = useTransform; + exports.useUnmountEffect = useUnmountEffect; + exports.useVelocity = useVelocity; + exports.useViewportScroll = useViewportScroll; + exports.useWillChange = useWillChange; + exports.velocityPerSecond = velocityPerSecond; + exports.vh = vh; + exports.visualElementStore = visualElementStore; + exports.vw = vw; + exports.warnOnce = warnOnce; + exports.wrap = wrap; + +})); diff --git a/node_modules/framer-motion/dist/framer-motion.js b/node_modules/framer-motion/dist/framer-motion.js new file mode 100644 index 00000000..756a313b --- /dev/null +++ b/node_modules/framer-motion/dist/framer-motion.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Motion={},t.React)}(this,function(t,e){"use strict";function n(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(n){if("default"!==n){var i=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,i.get?i:{enumerable:!0,get:function(){return t[n]}})}}),e.default=t,Object.freeze(e)}var i=n(e),s=React,o=Symbol.for("react.element"),r=Symbol.for("react.fragment"),a=Object.prototype.hasOwnProperty,l=s.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,u={key:!0,ref:!0,__self:!0,__source:!0};function c(t,e,n){var i,s={},r=null,c=null;for(i in void 0!==n&&(r=""+n),void 0!==e.key&&(r=""+e.key),void 0!==e.ref&&(c=e.ref),e)a.call(e,i)&&!u.hasOwnProperty(i)&&(s[i]=e[i]);if(t&&t.defaultProps)for(i in e=t.defaultProps)void 0===s[i]&&(s[i]=e[i]);return{$$typeof:o,type:t,key:r,ref:c,props:s,_owner:l.current}}const h=r,d=c,p=c,m=e.createContext({});function f(t){const n=e.useRef(null);return null===n.current&&(n.current=t()),n.current}const g="undefined"!=typeof window,y=g?e.useLayoutEffect:e.useEffect,v=e.createContext(null);function x(t,e){-1===t.indexOf(e)&&t.push(e)}function w(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}function T([...t],e,n){const i=e<0?t.length+e:e;if(i>=0&&in>e?e:n{},b=()=>{};const E={},A=t=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t);function M(t){return"object"==typeof t&&null!==t}const V=t=>/^0[^.\s]+$/u.test(t);function C(t){let e;return()=>(void 0===e&&(e=t()),e)}const R=t=>t,D=(t,e)=>n=>e(t(n)),k=(...t)=>t.reduce(D),L=(t,e,n)=>{const i=e-t;return 0===i?1:(n-t)/i};class O{constructor(){this.subscriptions=[]}add(t){return x(this.subscriptions,t),()=>w(this.subscriptions,t)}notify(t,e,n){const i=this.subscriptions.length;if(i)if(1===i)this.subscriptions[0](t,e,n);else for(let s=0;s1e3*t,B=t=>t/1e3;function F(t,e){return e?t*(1e3/e):0}const I=new Set;const W=(t,e,n)=>{const i=e-t;return((n-t)%i+i)%i+t},U=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function N(t,e,n,i){if(t===e&&n===i)return R;const s=e=>function(t,e,n,i,s){let o,r,a=0;do{r=e+(n-e)/2,o=U(r,i,s)-t,o>0?n=r:e=r}while(Math.abs(o)>1e-7&&++a<12);return r}(e,0,1,t,n);return t=>0===t||1===t?t:U(s(t),e,i)}const $=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,z=t=>e=>1-t(1-e),X=N(.33,1.53,.69,.99),Y=z(X),H=$(Y),K=t=>(t*=2)<1?.5*Y(t):.5*(2-Math.pow(2,-10*(t-1))),G=t=>1-Math.sin(Math.acos(t)),_=z(G),q=$(G),Z=N(.42,0,1,1),J=N(0,0,.58,1),Q=N(.42,0,.58,1);const tt=t=>Array.isArray(t)&&"number"!=typeof t[0];function et(t,e){return tt(t)?t[W(0,t.length,e)]:t}const nt=t=>Array.isArray(t)&&"number"==typeof t[0],it={linear:R,easeIn:Z,easeInOut:Q,easeOut:J,circIn:G,circInOut:q,circOut:_,backIn:Y,backInOut:H,backOut:X,anticipate:K},st=t=>{if(nt(t)){t.length;const[e,n,i,s]=t;return N(e,n,i,s)}return"string"==typeof t?it[t]:t},ot=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],rt={value:null,addProjectionMetrics:null};function at(t,e){let n=!1,i=!0;const s={delta:0,timestamp:0,isProcessing:!1},o=()=>n=!0,r=ot.reduce((t,n)=>(t[n]=function(t,e){let n=new Set,i=new Set,s=!1,o=!1;const r=new WeakSet;let a={delta:0,timestamp:0,isProcessing:!1},l=0;function u(e){r.has(e)&&(c.schedule(e),t()),l++,e(a)}const c={schedule:(t,e=!1,o=!1)=>{const a=o&&s?n:i;return e&&r.add(t),a.has(t)||a.add(t),t},cancel:t=>{i.delete(t),r.delete(t)},process:t=>{a=t,s?o=!0:(s=!0,[n,i]=[i,n],n.forEach(u),e&&rt.value&&rt.value.frameloop[e].push(l),l=0,n.clear(),s=!1,o&&(o=!1,c.process(t)))}};return c}(o,e?n:void 0),t),{}),{setup:a,read:l,resolveKeyframes:u,preUpdate:c,update:h,preRender:d,render:p,postRender:m}=r,f=()=>{const o=E.useManualTiming?s.timestamp:performance.now();n=!1,E.useManualTiming||(s.delta=i?1e3/60:Math.max(Math.min(o-s.timestamp,40),1)),s.timestamp=o,s.isProcessing=!0,a.process(s),l.process(s),u.process(s),c.process(s),h.process(s),d.process(s),p.process(s),m.process(s),s.isProcessing=!1,n&&e&&(i=!1,t(f))};return{schedule:ot.reduce((e,o)=>{const a=r[o];return e[o]=(e,o=!1,r=!1)=>(n||(n=!0,i=!0,s.isProcessing||t(f)),a.schedule(e,o,r)),e},{}),cancel:t=>{for(let e=0;e(void 0===dt&&mt.set(ct.isProcessing||E.useManualTiming?ct.timestamp:performance.now()),dt),set:t=>{dt=t,queueMicrotask(pt)}},ft={layout:0,mainThread:0,waapi:0},gt=t=>e=>"string"==typeof e&&e.startsWith(t),yt=gt("--"),vt=gt("var(--"),xt=t=>!!vt(t)&&wt.test(t.split("/*")[0].trim()),wt=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,Tt={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},Pt={...Tt,transform:t=>P(0,1,t)},St={...Tt,default:1},bt=t=>Math.round(1e5*t)/1e5,Et=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const At=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,Mt=(t,e)=>n=>Boolean("string"==typeof n&&At.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),Vt=(t,e,n)=>i=>{if("string"!=typeof i)return i;const[s,o,r,a]=i.match(Et);return{[t]:parseFloat(s),[e]:parseFloat(o),[n]:parseFloat(r),alpha:void 0!==a?parseFloat(a):1}},Ct={...Tt,transform:t=>Math.round((t=>P(0,255,t))(t))},Rt={test:Mt("rgb","red"),parse:Vt("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:i=1})=>"rgba("+Ct.transform(t)+", "+Ct.transform(e)+", "+Ct.transform(n)+", "+bt(Pt.transform(i))+")"};const Dt={test:Mt("#"),parse:function(t){let e="",n="",i="",s="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),i=t.substring(5,7),s=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),i=t.substring(3,4),s=t.substring(4,5),e+=e,n+=n,i+=i,s+=s),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(i,16),alpha:s?parseInt(s,16)/255:1}},transform:Rt.transform},kt=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),Lt=kt("deg"),Ot=kt("%"),jt=kt("px"),Bt=kt("vh"),Ft=kt("vw"),It=(()=>({...Ot,parse:t=>Ot.parse(t)/100,transform:t=>Ot.transform(100*t)}))(),Wt={test:Mt("hsl","hue"),parse:Vt("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:i=1})=>"hsla("+Math.round(t)+", "+Ot.transform(bt(e))+", "+Ot.transform(bt(n))+", "+bt(Pt.transform(i))+")"},Ut={test:t=>Rt.test(t)||Dt.test(t)||Wt.test(t),parse:t=>Rt.test(t)?Rt.parse(t):Wt.test(t)?Wt.parse(t):Dt.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?Rt.transform(t):Wt.transform(t),getAnimatableNone:t=>{const e=Ut.parse(t);return e.alpha=0,Ut.transform(e)}},Nt=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const $t="number",zt="color",Xt=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Yt(t){const e=t.toString(),n=[],i={color:[],number:[],var:[]},s=[];let o=0;const r=e.replace(Xt,t=>(Ut.test(t)?(i.color.push(o),s.push(zt),n.push(Ut.parse(t))):t.startsWith("var(")?(i.var.push(o),s.push("var"),n.push(t)):(i.number.push(o),s.push($t),n.push(parseFloat(t))),++o,"${}")).split("${}");return{values:n,split:r,indexes:i,types:s}}function Ht(t){return Yt(t).values}function Kt(t){const{split:e,types:n}=Yt(t),i=e.length;return t=>{let s="";for(let o=0;o"number"==typeof t?0:Ut.test(t)?Ut.getAnimatableNone(t):t;const _t={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(Et)?.length||0)+(t.match(Nt)?.length||0)>0},parse:Ht,createTransformer:Kt,getAnimatableNone:function(t){const e=Ht(t);return Kt(t)(e.map(Gt))}};function qt(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function Zt({hue:t,saturation:e,lightness:n,alpha:i}){t/=360,n/=100;let s=0,o=0,r=0;if(e/=100){const i=n<.5?n*(1+e):n+e-n*e,a=2*n-i;s=qt(a,i,t+1/3),o=qt(a,i,t),r=qt(a,i,t-1/3)}else s=o=r=n;return{red:Math.round(255*s),green:Math.round(255*o),blue:Math.round(255*r),alpha:i}}function Jt(t,e){return n=>n>0?e:t}const Qt=(t,e,n)=>t+(e-t)*n,te=(t,e,n)=>{const i=t*t,s=n*(e*e-i)+i;return s<0?0:Math.sqrt(s)},ee=[Dt,Rt,Wt];function ne(t){const e=(n=t,ee.find(t=>t.test(n)));var n;if(!Boolean(e))return!1;let i=e.parse(t);return e===Wt&&(i=Zt(i)),i}const ie=(t,e)=>{const n=ne(t),i=ne(e);if(!n||!i)return Jt(t,e);const s={...n};return t=>(s.red=te(n.red,i.red,t),s.green=te(n.green,i.green,t),s.blue=te(n.blue,i.blue,t),s.alpha=Qt(n.alpha,i.alpha,t),Rt.transform(s))},se=new Set(["none","hidden"]);function oe(t,e){return se.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}function re(t,e){return n=>Qt(t,e,n)}function ae(t){return"number"==typeof t?re:"string"==typeof t?xt(t)?Jt:Ut.test(t)?ie:ce:Array.isArray(t)?le:"object"==typeof t?Ut.test(t)?ie:ue:Jt}function le(t,e){const n=[...t],i=n.length,s=t.map((t,n)=>ae(t)(t,e[n]));return t=>{for(let e=0;e{for(const e in i)n[e]=i[e](t);return n}}const ce=(t,e)=>{const n=_t.createTransformer(e),i=Yt(t),s=Yt(e);return i.indexes.var.length===s.indexes.var.length&&i.indexes.color.length===s.indexes.color.length&&i.indexes.number.length>=s.indexes.number.length?se.has(t)&&!s.values.length||se.has(e)&&!i.values.length?oe(t,e):k(le(function(t,e){const n=[],i={color:0,var:0,number:0};for(let s=0;s{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>lt.update(e,t),stop:()=>ut(e),now:()=>ct.isProcessing?ct.timestamp:mt.now()}},pe=(t,e,n=10)=>{let i="";const s=Math.max(Math.round(e/n),2);for(let e=0;e=me?1/0:e}function ge(t,e=100,n){const i=n({...t,keyframes:[0,e]}),s=Math.min(fe(i),me);return{type:"keyframes",ease:t=>i.next(s*t).value/e,duration:B(s)}}function ye(t,e,n){const i=Math.max(e-5,0);return F(n-t(i),e-i)}const ve=100,xe=10,we=1,Te=0,Pe=800,Se=.3,be=.3,Ee={granular:.01,default:2},Ae={granular:.005,default:.5},Me=.01,Ve=10,Ce=.05,Re=1,De=.001;function ke({duration:t=Pe,bounce:e=Se,velocity:n=Te,mass:i=we}){let s,o,r=1-e;r=P(Ce,Re,r),t=P(Me,Ve,B(t)),r<1?(s=e=>{const i=e*r,s=i*t,o=i-n,a=Oe(e,r),l=Math.exp(-s);return De-o/a*l},o=e=>{const i=e*r*t,o=i*n+n,a=Math.pow(r,2)*Math.pow(e,2)*t,l=Math.exp(-i),u=Oe(Math.pow(e,2),r);return(-s(e)+De>0?-1:1)*((o-a)*l)/u}):(s=e=>Math.exp(-e*t)*((e-n)*t+1)-.001,o=e=>Math.exp(-e*t)*(t*t*(n-e)));const a=function(t,e,n){let i=n;for(let n=1;nvoid 0!==t[e])}function Ie(t=be,e=Se){const n="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:i,restDelta:s}=n;const o=n.keyframes[0],r=n.keyframes[n.keyframes.length-1],a={done:!1,value:o},{stiffness:l,damping:u,mass:c,duration:h,velocity:d,isResolvedFromDuration:p}=function(t){let e={velocity:Te,stiffness:ve,damping:xe,mass:we,isResolvedFromDuration:!1,...t};if(!Fe(t,Be)&&Fe(t,je))if(t.visualDuration){const n=t.visualDuration,i=2*Math.PI/(1.2*n),s=i*i,o=2*P(.05,1,1-(t.bounce||0))*Math.sqrt(s);e={...e,mass:we,stiffness:s,damping:o}}else{const n=ke(t);e={...e,...n,mass:we},e.isResolvedFromDuration=!0}return e}({...n,velocity:-B(n.velocity||0)}),m=d||0,f=u/(2*Math.sqrt(l*c)),g=r-o,y=B(Math.sqrt(l/c)),v=Math.abs(g)<5;let x;if(i||(i=v?Ee.granular:Ee.default),s||(s=v?Ae.granular:Ae.default),f<1){const t=Oe(y,f);x=e=>{const n=Math.exp(-f*y*e);return r-n*((m+f*y*g)/t*Math.sin(t*e)+g*Math.cos(t*e))}}else if(1===f)x=t=>r-Math.exp(-y*t)*(g+(m+y*g)*t);else{const t=y*Math.sqrt(f*f-1);x=e=>{const n=Math.exp(-f*y*e),i=Math.min(t*e,300);return r-n*((m+f*y*g)*Math.sinh(i)+t*g*Math.cosh(i))/t}}const w={calculatedDuration:p&&h||null,next:t=>{const e=x(t);if(p)a.done=t>=h;else{let n=0===t?m:0;f<1&&(n=0===t?j(m):ye(x,t,e));const o=Math.abs(n)<=i,l=Math.abs(r-e)<=s;a.done=o&&l}return a.value=a.done?r:e,a},toString:()=>{const t=Math.min(fe(w),me),e=pe(e=>w.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return w}function We({keyframes:t,velocity:e=0,power:n=.8,timeConstant:i=325,bounceDamping:s=10,bounceStiffness:o=500,modifyTarget:r,min:a,max:l,restDelta:u=.5,restSpeed:c}){const h=t[0],d={done:!1,value:h},p=t=>void 0===a?l:void 0===l||Math.abs(a-t)-m*Math.exp(-t/i),v=t=>g+y(t),x=t=>{const e=y(t),n=v(t);d.done=Math.abs(e)<=u,d.value=d.done?g:n};let w,T;const P=t=>{var e;(e=d.value,void 0!==a&&el)&&(w=t,T=Ie({keyframes:[d.value,p(d.value)],velocity:ye(v,t,d.value),damping:s,stiffness:o,restDelta:u,restSpeed:c}))};return P(0),{calculatedDuration:null,next:t=>{let e=!1;return T||void 0!==w||(e=!0,x(t),P(t)),void 0!==w&&t>=w?T.next(t-w):(!e&&x(t),d)}}}function Ue(t,e,{clamp:n=!0,ease:i,mixer:s}={}){const o=t.length;if(e.length,1===o)return()=>e[0];if(2===o&&e[0]===e[1])return()=>e[1];const r=t[0]===t[1];t[0]>t[o-1]&&(t=[...t].reverse(),e=[...e].reverse());const a=function(t,e,n){const i=[],s=n||E.mix||he,o=t.length-1;for(let n=0;n{if(r&&n1)for(;iu(P(t[0],t[o-1],e)):u}function Ne(t,e){const n=t[t.length-1];for(let i=1;i<=e;i++){const s=L(0,e,i);t.push(Qt(n,1,s))}}function $e(t){const e=[0];return Ne(e,t.length-1),e}function ze(t,e){return t.map(t=>t*e)}function Xe(t,e){return t.map(()=>e||Q).splice(0,t.length-1)}function Ye({duration:t=300,keyframes:e,times:n,ease:i="easeInOut"}){const s=tt(i)?i.map(st):st(i),o={done:!1,value:e[0]},r=Ue(ze(n&&n.length===e.length?n:$e(e),t),e,{ease:Array.isArray(s)?s:Xe(e,s)});return{calculatedDuration:t,next:e=>(o.value=r(e),o.done=e>=t,o)}}Ie.applyToOptions=t=>{const e=ge(t,100,Ie);return t.ease=e.ease,t.duration=j(e.duration),t.type="keyframes",t};const He=t=>null!==t;function Ke(t,{repeat:e,repeatType:n="loop"},i,s=1){const o=t.filter(He),r=s<0||e&&"loop"!==n&&e%2==1?0:o.length-1;return r&&void 0!==i?i:o[r]}const Ge={decay:We,inertia:We,tween:Ye,keyframes:Ye,spring:Ie};function _e(t){"string"==typeof t.type&&(t.type=Ge[t.type])}class qe{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Ze=t=>t/100;class Je extends qe{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==mt.now()&&this.tick(mt.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},ft.mainThread++,this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;_e(t);const{type:e=Ye,repeat:n=0,repeatDelay:i=0,repeatType:s,velocity:o=0}=t;let{keyframes:r}=t;const a=e||Ye;a!==Ye&&"number"!=typeof r[0]&&(this.mixKeyframes=k(Ze,he(r[0],r[1])),r=[0,100]);const l=a({...t,keyframes:r});"mirror"===s&&(this.mirroredGenerator=a({...t,keyframes:[...r].reverse(),velocity:-o})),null===l.calculatedDuration&&(l.calculatedDuration=fe(l));const{calculatedDuration:u}=l;this.calculatedDuration=u,this.resolvedDuration=u+i,this.totalDuration=this.resolvedDuration*(n+1)-i,this.generator=l}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,e=!1){const{generator:n,totalDuration:i,mixKeyframes:s,mirroredGenerator:o,resolvedDuration:r,calculatedDuration:a}=this;if(null===this.startTime)return n.next(0);const{delay:l=0,keyframes:u,repeat:c,repeatType:h,repeatDelay:d,type:p,onUpdate:m,finalKeyframe:f}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-i/this.speed,this.startTime)),e?this.currentTime=t:this.updateTime(t);const g=this.currentTime-l*(this.playbackSpeed>=0?1:-1),y=this.playbackSpeed>=0?g<0:g>i;this.currentTime=Math.max(g,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=i);let v=this.currentTime,x=n;if(c){const t=Math.min(this.currentTime,i)/r;let e=Math.floor(t),n=t%1;!n&&t>=1&&(n=1),1===n&&e--,e=Math.min(e,c+1);Boolean(e%2)&&("reverse"===h?(n=1-n,d&&(n-=d/r)):"mirror"===h&&(x=o)),v=P(0,1,n)*r}const w=y?{done:!1,value:u[0]}:x.next(v);s&&(w.value=s(w.value));let{done:T}=w;y||null===a||(T=this.playbackSpeed>=0?this.currentTime>=i:this.currentTime<=0);const S=null===this.holdTime&&("finished"===this.state||"running"===this.state&&T);return S&&p!==We&&(w.value=Ke(u,this.options,f,this.speed)),m&&m(w.value),S&&this.finish(),w}then(t,e){return this.finished.then(t,e)}get duration(){return B(this.calculatedDuration)}get time(){return B(this.currentTime)}set time(t){t=j(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(mt.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=B(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=de,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const n=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=n):null!==this.holdTime?this.startTime=n-this.holdTime:this.startTime||(this.startTime=e??n),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(mt.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null,ft.mainThread--}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}function Qe(t){for(let e=1;e180*t/Math.PI,en=t=>{const e=tn(Math.atan2(t[1],t[0]));return sn(e)},nn={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:en,rotateZ:en,skewX:t=>tn(Math.atan(t[1])),skewY:t=>tn(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},sn=t=>((t%=360)<0&&(t+=360),t),on=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),rn=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),an={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:on,scaleY:rn,scale:t=>(on(t)+rn(t))/2,rotateX:t=>sn(tn(Math.atan2(t[6],t[5]))),rotateY:t=>sn(tn(Math.atan2(-t[2],t[0]))),rotateZ:en,rotate:en,skewX:t=>tn(Math.atan(t[4])),skewY:t=>tn(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function ln(t){return t.includes("scale")?1:0}function un(t,e){if(!t||"none"===t)return ln(e);const n=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let i,s;if(n)i=an,s=n;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);i=nn,s=e}if(!s)return ln(e);const o=i[e],r=s[1].split(",").map(hn);return"function"==typeof o?o(r):r[o]}const cn=(t,e)=>{const{transform:n="none"}=getComputedStyle(t);return un(n,e)};function hn(t){return parseFloat(t.trim())}const dn=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],pn=(()=>new Set(dn))(),mn=t=>t===Tt||t===jt,fn=new Set(["x","y","z"]),gn=dn.filter(t=>!fn.has(t));const yn={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>un(e,"x"),y:(t,{transform:e})=>un(e,"y")};yn.translateX=yn.x,yn.translateY=yn.y;const vn=new Set;let xn=!1,wn=!1,Tn=!1;function Pn(){if(wn){const t=Array.from(vn).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return gn.forEach(n=>{const i=t.getValue(n);void 0!==i&&(e.push([n,i.get()]),i.set(n.startsWith("scale")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{t.getValue(e)?.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}wn=!1,xn=!1,vn.forEach(t=>t.complete(Tn)),vn.clear()}function Sn(){vn.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(wn=!0)})}function bn(){Tn=!0,Sn(),Pn(),Tn=!1}class En{constructor(t,e,n,i,s,o=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=i,this.element=s,this.isAsync=o}scheduleResolve(){this.state="scheduled",this.isAsync?(vn.add(this),xn||(xn=!0,lt.read(Sn),lt.resolveKeyframes(Pn))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:i}=this;if(null===t[0]){const s=i?.get(),o=t[t.length-1];if(void 0!==s)t[0]=s;else if(n&&e){const i=n.readValue(e,o);null!=i&&(t[0]=i)}void 0===t[0]&&(t[0]=o),i&&void 0===s&&i.set(t[0])}Qe(t)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(t=!1){this.state="complete",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,t),vn.delete(this)}cancel(){"scheduled"===this.state&&(vn.delete(this),this.state="pending")}resume(){"pending"===this.state&&this.scheduleResolve()}}const An=t=>t.startsWith("--");function Mn(t,e,n){An(e)?t.style.setProperty(e,n):t.style[e]=n}const Vn=C(()=>void 0!==window.ScrollTimeline),Cn={};function Rn(t,e){const n=C(t);return()=>Cn[e]??n()}const Dn=Rn(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),kn=([t,e,n,i])=>`cubic-bezier(${t}, ${e}, ${n}, ${i})`,Ln={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:kn([0,.65,.55,1]),circOut:kn([.55,0,1,.45]),backIn:kn([.31,.01,.66,-.59]),backOut:kn([.33,1.53,.69,.99])};function On(t,e){return t?"function"==typeof t?Dn()?pe(t,e):"ease-out":nt(t)?kn(t):Array.isArray(t)?t.map(t=>On(t,e)||Ln.easeOut):Ln[t]:void 0}function jn(t,e,n,{delay:i=0,duration:s=300,repeat:o=0,repeatType:r="loop",ease:a="easeOut",times:l}={},u=void 0){const c={[e]:n};l&&(c.offset=l);const h=On(a,s);Array.isArray(h)&&(c.easing=h),rt.value&&ft.waapi++;const d={delay:i,duration:s,easing:Array.isArray(h)?"linear":h,fill:"both",iterations:o+1,direction:"reverse"===r?"alternate":"normal"};u&&(d.pseudoElement=u);const p=t.animate(c,d);return rt.value&&p.finished.finally(()=>{ft.waapi--}),p}function Bn(t){return"function"==typeof t&&"applyToOptions"in t}function Fn({type:t,...e}){return Bn(t)&&Dn()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}class In extends qe{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:n,keyframes:i,pseudoElement:s,allowFlatten:o=!1,finalKeyframe:r,onComplete:a}=t;this.isPseudoElement=Boolean(s),this.allowFlatten=o,this.options=t,t.type;const l=Fn(t);this.animation=jn(e,n,i,l,s),!1===l.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!s){const t=Ke(i,this.options,r,this.speed);this.updateMotionValue?this.updateMotionValue(t):Mn(e,n,t),this.animation.cancel()}a?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return B(Number(t))}get time(){return B(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=j(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&Vn()?(this.animation.timeline=t,R):e(this)}}const Wn={anticipate:K,backInOut:H,circInOut:q};function Un(t){"string"==typeof t.ease&&t.ease in Wn&&(t.ease=Wn[t.ease])}class Nn extends In{constructor(t){Un(t),_e(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:n,onComplete:i,element:s,...o}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const r=new Je({...o,autoplay:!1}),a=j(this.finishedTime??this.time);e.setWithVelocity(r.sample(a-10).value,r.sample(a).value,10),r.stop()}}const $n=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!_t.test(t)&&"0"!==t||t.startsWith("url(")));function zn(t){return M(t)&&"offsetHeight"in t}const Xn=new Set(["opacity","clipPath","filter","transform"]),Yn=C(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));function Hn(t){const{motionValue:e,name:n,repeatDelay:i,repeatType:s,damping:o,type:r}=t;if(!zn(e?.owner?.current))return!1;const{onUpdate:a,transformTemplate:l}=e.owner.getProps();return Yn()&&n&&Xn.has(n)&&("transform"!==n||!l)&&!a&&!i&&"mirror"!==s&&0!==o&&"inertia"!==r}class Kn extends qe{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:i=0,repeatDelay:s=0,repeatType:o="loop",keyframes:r,name:a,motionValue:l,element:u,...c}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=mt.now();const h={autoplay:t,delay:e,type:n,repeat:i,repeatDelay:s,repeatType:o,name:a,motionValue:l,element:u,...c},d=u?.KeyframeResolver||En;this.keyframeResolver=new d(r,(t,e,n)=>this.onKeyframesResolved(t,e,h,!n),a,l,u),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,e,n,i){this.keyframeResolver=void 0;const{name:s,type:o,velocity:r,delay:a,isHandoff:l,onUpdate:u}=n;this.resolvedAt=mt.now(),function(t,e,n,i){const s=t[0];if(null===s)return!1;if("display"===e||"visibility"===e)return!0;const o=t[t.length-1],r=$n(s,e),a=$n(o,e);return!(!r||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:e,...n,keyframes:t},h=!l&&Hn(c)?new Nn({...c,element:c.motionValue.owner.current}):new Je(c);h.finished.then(()=>this.notifyFinished()).catch(R),this.pendingTimeline&&(this.stopTimeline=h.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=h}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),bn()),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}}class Gn{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let n=0;ne.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class _n extends Gn{then(t,e){return this.finished.finally(t).then(()=>{})}}class qn extends In{constructor(t){super(),this.animation=t,t.onfinish=()=>{this.finishedTime=this.time,this.notifyFinished()}}}const Zn=new WeakMap,Jn=(t,e="")=>`${t}:${e}`;function Qn(t){const e=Zn.get(t)||new Map;return Zn.set(t,e),e}const ti=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function ei(t){const e=ti.exec(t);if(!e)return[,];const[,n,i,s]=e;return[`--${n??i}`,s]}function ni(t,e,n=1){const[i,s]=ei(t);if(!i)return;const o=window.getComputedStyle(e).getPropertyValue(i);if(o){const t=o.trim();return A(t)?parseFloat(t):t}return xt(s)?ni(s,e,n+1):s}function ii(t,e){return t?.[e]??t?.default??t}const si=new Set(["width","height","top","left","right","bottom",...dn]),oi=t=>e=>e.test(t),ri=[Tt,jt,Ot,Lt,Ft,Bt,{test:t=>"auto"===t,parse:t=>t}],ai=t=>ri.find(oi(t));function li(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||V(t))}const ui=new Set(["brightness","contrast","saturate","opacity"]);function ci(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[i]=n.match(Et)||[];if(!i)return t;const s=n.replace(i,"");let o=ui.has(e)?1:0;return i!==n&&(o*=100),e+"("+o+s+")"}const hi=/\b([a-z-]*)\(.*?\)/gu,di={..._t,getAnimatableNone:t=>{const e=t.match(hi);return e?e.map(ci).join(" "):t}},pi={...Tt,transform:Math.round},mi={rotate:Lt,rotateX:Lt,rotateY:Lt,rotateZ:Lt,scale:St,scaleX:St,scaleY:St,scaleZ:St,skew:Lt,skewX:Lt,skewY:Lt,distance:jt,translateX:jt,translateY:jt,translateZ:jt,x:jt,y:jt,z:jt,perspective:jt,transformPerspective:jt,opacity:Pt,originX:It,originY:It,originZ:jt},fi={borderWidth:jt,borderTopWidth:jt,borderRightWidth:jt,borderBottomWidth:jt,borderLeftWidth:jt,borderRadius:jt,radius:jt,borderTopLeftRadius:jt,borderTopRightRadius:jt,borderBottomRightRadius:jt,borderBottomLeftRadius:jt,width:jt,maxWidth:jt,height:jt,maxHeight:jt,top:jt,right:jt,bottom:jt,left:jt,padding:jt,paddingTop:jt,paddingRight:jt,paddingBottom:jt,paddingLeft:jt,margin:jt,marginTop:jt,marginRight:jt,marginBottom:jt,marginLeft:jt,backgroundPositionX:jt,backgroundPositionY:jt,...mi,zIndex:pi,fillOpacity:Pt,strokeOpacity:Pt,numOctaves:pi},gi={...fi,color:Ut,backgroundColor:Ut,outlineColor:Ut,fill:Ut,stroke:Ut,borderColor:Ut,borderTopColor:Ut,borderRightColor:Ut,borderBottomColor:Ut,borderLeftColor:Ut,filter:di,WebkitFilter:di},yi=t=>gi[t];function vi(t,e){let n=yi(t);return n!==di&&(n=_t),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const xi=new Set(["auto","none","0"]);class wi extends En{constructor(t,e,n,i,s){super(t,e,n,i,s,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{t.getValue(e).set(n)}),this.resolveNoneKeyframes()}}const Ti=new Set(["borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderRadius","radius","borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius","width","maxWidth","height","maxHeight","top","right","bottom","left","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","backgroundPositionX","backgroundPositionY"]);function Pi(t,e){for(let n=0;n{try{document.createElement("div").animate({opacity:[1]})}catch(t){return!1}return!0}),bi=new Set(["opacity","clipPath","filter","transform"]);function Ei(t,e,n){if(t instanceof EventTarget)return[t];if("string"==typeof t){let i=document;e&&(i=e.current);const s=n?.[t]??i.querySelectorAll(t);return s?Array.from(s):[]}return Array.from(t)}function Ai(t){return(e,n)=>{const i=Ei(e),s=[];for(const e of i){const i=t(e,n);s.push(i)}return()=>{for(const t of s)t()}}}const Mi=(t,e)=>e&&"number"==typeof t?e.transform(t):t;class Vi{constructor(){this.latest={},this.values=new Map}set(t,e,n,i,s=!0){const o=this.values.get(t);o&&o.onRemove();const r=()=>{const i=e.get();this.latest[t]=s?Mi(i,fi[t]):i,n&<.render(n)};r();const a=e.on("change",r);i&&e.addDependent(i);const l=()=>{a(),n&&ut(n),this.values.delete(t),i&&e.removeDependent(i)};return this.values.set(t,{value:e,onRemove:l}),l}get(t){return this.values.get(t)?.value}destroy(){for(const t of this.values.values())t.onRemove()}}function Ci(t){const e=new WeakMap,n=[];return(i,s)=>{const o=e.get(i)??new Vi;e.set(i,o);for(const e in s){const r=s[e],a=t(i,o,e,r);n.push(a)}return()=>{for(const t of n)t()}}}const Ri=(t,e,n,i)=>{const s=function(t,e){if(!(e in t))return!1;const n=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(t),e)||Object.getOwnPropertyDescriptor(t,e);return n&&"function"==typeof n.set}(t,n),o=s?n:n.startsWith("data")||n.startsWith("aria")?n.replace(/([A-Z])/g,t=>`-${t.toLowerCase()}`):n;const r=s?()=>{t[o]=e.latest[n]}:()=>{const i=e.latest[n];null==i?t.removeAttribute(o):t.setAttribute(o,String(i))};return e.set(n,i,r)},Di=Ai(Ci(Ri)),ki=Ci((t,e,n,i)=>e.set(n,i,()=>{t[n]=e.latest[n]},void 0,!1)),Li={current:void 0};class Oi{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const n=mt.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=mt.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new O);const n=this.events[t].add(e);return"change"===t?()=>{n(),lt.read(()=>{this.events.change.getSize()||this.stop()})}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return Li.current&&Li.current.push(this),this.current}getPrevious(){return this.prev}getVelocity(){const t=mt.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return F(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function ji(t,e){return new Oi(t,e)}const Bi={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"};const Fi=new Set(["originX","originY","originZ"]),Ii=(t,e,n,i)=>{let s,o;return pn.has(n)?(e.get("transform")||(zn(t)||e.get("transformBox")||Ii(t,e,"transformBox",new Oi("fill-box")),e.set("transform",new Oi("none"),()=>{t.style.transform=function(t){let e="",n=!0;for(let i=0;i{const n=e.latest.originX??"50%",i=e.latest.originY??"50%",s=e.latest.originZ??0;t.style.transformOrigin=`${n} ${i} ${s}`}),o=e.get("transformOrigin")):s=An(n)?()=>{t.style.setProperty(n,e.latest[n])}:()=>{t.style[n]=e.latest[n]},e.set(n,i,s,o)},Wi=Ai(Ci(Ii)),Ui=jt.transform;const Ni=Ai(Ci((t,e,n,i)=>{if(n.startsWith("path"))return function(t,e,n,i){return lt.render(()=>t.setAttribute("pathLength","1")),"pathOffset"===n?e.set(n,i,()=>t.setAttribute("stroke-dashoffset",Ui(-e.latest[n]))):(e.get("stroke-dasharray")||e.set("stroke-dasharray",new Oi("1 1"),()=>{const{pathLength:n=1,pathSpacing:i}=e.latest;t.setAttribute("stroke-dasharray",`${Ui(n)} ${Ui(i??1-Number(n))}`)}),e.set(n,i,void 0,e.get("stroke-dasharray")))}(t,e,n,i);if(n.startsWith("attr"))return Ri(t,e,function(t){return t.replace(/^attr([A-Z])/,(t,e)=>e.toLowerCase())}(n),i);return(n in t.style?Ii:Ri)(t,e,n,i)}));const{schedule:$i,cancel:zi}=at(queueMicrotask,!1),Xi={x:!1,y:!1};function Yi(){return Xi.x||Xi.y}function Hi(t){return"x"===t||"y"===t?Xi[t]?null:(Xi[t]=!0,()=>{Xi[t]=!1}):Xi.x||Xi.y?null:(Xi.x=Xi.y=!0,()=>{Xi.x=Xi.y=!1})}function Ki(t,e){const n=Ei(t),i=new AbortController;return[n,{passive:!0,...e,signal:i.signal},()=>i.abort()]}function Gi(t){return!("touch"===t.pointerType||Yi())}function _i(t,e,n={}){const[i,s,o]=Ki(t,n),r=t=>{if(!Gi(t))return;const{target:n}=t,i=e(n,t);if("function"!=typeof i||!n)return;const o=t=>{Gi(t)&&(i(t),n.removeEventListener("pointerleave",o))};n.addEventListener("pointerleave",o,s)};return i.forEach(t=>{t.addEventListener("pointerenter",r,s)}),o}const qi=(t,e)=>!!e&&(t===e||qi(t,e.parentElement)),Zi=t=>"mouse"===t.pointerType?"number"!=typeof t.button||t.button<=0:!1!==t.isPrimary,Ji=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);const Qi=new WeakSet;function ts(t){return e=>{"Enter"===e.key&&t(e)}}function es(t,e){t.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}function ns(t){return Zi(t)&&!Yi()}function is(t,e,n={}){const[i,s,o]=Ki(t,n),r=t=>{const i=t.currentTarget;if(!ns(t))return;Qi.add(i);const o=e(i,t),r=(t,e)=>{window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",l),Qi.has(i)&&Qi.delete(i),ns(t)&&"function"==typeof o&&o(t,{success:e})},a=t=>{r(t,i===window||i===document||n.useGlobalTarget||qi(i,t.target))},l=t=>{r(t,!1)};window.addEventListener("pointerup",a,s),window.addEventListener("pointercancel",l,s)};return i.forEach(t=>{var e;(n.useGlobalTarget?window:t).addEventListener("pointerdown",r,s),zn(t)&&(t.addEventListener("focus",t=>((t,e)=>{const n=t.currentTarget;if(!n)return;const i=ts(()=>{if(Qi.has(n))return;es(n,"down");const t=ts(()=>{es(n,"up")});n.addEventListener("keyup",t,e),n.addEventListener("blur",()=>es(n,"cancel"),e)});n.addEventListener("keydown",i,e),n.addEventListener("blur",()=>n.removeEventListener("keydown",i),e)})(t,s)),e=t,Ji.has(e.tagName)||-1!==e.tabIndex||t.hasAttribute("tabindex")||(t.tabIndex=0))}),o}function ss(t,e){const n=window.getComputedStyle(t);return An(e)?n.getPropertyValue(e):n[e]}function os(t){return M(t)&&"ownerSVGElement"in t}const rs=new WeakMap;let as;const ls=(t,e,n)=>(i,s)=>s&&s[0]?s[0][t+"Size"]:os(i)&&"getBBox"in i?i.getBBox()[e]:i[n],us=ls("inline","width","offsetWidth"),cs=ls("block","height","offsetHeight");function hs({target:t,borderBoxSize:e}){rs.get(t)?.forEach(n=>{n(t,{get width(){return us(t,e)},get height(){return cs(t,e)}})})}function ds(t){t.forEach(hs)}function ps(t,e){as||"undefined"!=typeof ResizeObserver&&(as=new ResizeObserver(ds));const n=Ei(t);return n.forEach(t=>{let n=rs.get(t);n||(n=new Set,rs.set(t,n)),n.add(e),as?.observe(t)}),()=>{n.forEach(t=>{const n=rs.get(t);n?.delete(e),n?.size||as?.unobserve(t)})}}const ms=new Set;let fs;function gs(t){return ms.add(t),fs||(fs=()=>{const t={get width(){return window.innerWidth},get height(){return window.innerHeight}};ms.forEach(e=>e(t))},window.addEventListener("resize",fs)),()=>{ms.delete(t),ms.size||"function"!=typeof fs||(window.removeEventListener("resize",fs),fs=void 0)}}function ys(t,e){return"function"==typeof t?gs(t):ps(t,e)}function vs(t,e){let n;const i=()=>{const{currentTime:i}=e,s=(null===i?0:i.value)/100;n!==s&&t(s),n=s};return lt.preUpdate(i,!0),()=>ut(i)}function xs(){const{value:t}=rt;null!==t?(t.frameloop.rate.push(ct.delta),t.animations.mainThread.push(ft.mainThread),t.animations.waapi.push(ft.waapi),t.animations.layout.push(ft.layout)):ut(xs)}function ws(t){return t.reduce((t,e)=>t+e,0)/t.length}function Ts(t,e=ws){return 0===t.length?{min:0,max:0,avg:0}:{min:Math.min(...t),max:Math.max(...t),avg:e(t)}}const Ps=t=>Math.round(1e3/t);function Ss(){rt.value=null,rt.addProjectionMetrics=null}function bs(){const{value:t}=rt;if(!t)throw new Error("Stats are not being measured");Ss(),ut(xs);const e={frameloop:{setup:Ts(t.frameloop.setup),rate:Ts(t.frameloop.rate),read:Ts(t.frameloop.read),resolveKeyframes:Ts(t.frameloop.resolveKeyframes),preUpdate:Ts(t.frameloop.preUpdate),update:Ts(t.frameloop.update),preRender:Ts(t.frameloop.preRender),render:Ts(t.frameloop.render),postRender:Ts(t.frameloop.postRender)},animations:{mainThread:Ts(t.animations.mainThread),waapi:Ts(t.animations.waapi),layout:Ts(t.animations.layout)},layoutProjection:{nodes:Ts(t.layoutProjection.nodes),calculatedTargetDeltas:Ts(t.layoutProjection.calculatedTargetDeltas),calculatedProjections:Ts(t.layoutProjection.calculatedProjections)}},{rate:n}=e.frameloop;return n.min=Ps(n.min),n.max=Ps(n.max),n.avg=Ps(n.avg),[n.min,n.max]=[n.max,n.min],e}function Es(t){return os(t)&&"svg"===t.tagName}function As(t,e){if("first"===t)return 0;{const n=e-1;return"last"===t?n:n/2}}function Ms(...t){const e=!Array.isArray(t[0]),n=e?0:-1,i=t[0+n],s=Ue(t[1+n],t[2+n],t[3+n]);return e?s(i):s}function Vs(t){const e=[];Li.current=e;const n=t();Li.current=void 0;const i=ji(n);return function(t,e,n){const i=()=>e.set(n()),s=()=>lt.preRender(i,!1,!0),o=t.map(t=>t.on("change",s));e.on("destroy",()=>{o.forEach(t=>t()),ut(i)})}(e,i,t),i}const Cs=t=>Boolean(t&&t.getVelocity);function Rs(t,e,n){const i=t.get();let s,o=null,r=i;const a="string"==typeof i?i.replace(/[\d.-]/g,""):void 0,l=()=>{o&&(o.stop(),o=null)},u=()=>{l(),o=new Je({keyframes:[ks(t.get()),ks(r)],velocity:t.getVelocity(),type:"spring",restDelta:.001,restSpeed:.01,...n,onUpdate:s})};let c;return t.attach((e,n)=>(r=e,s=t=>n(Ds(t,a)),lt.postRender(u),t.get()),l),Cs(e)&&(c=e.on("change",e=>t.set(Ds(e,a))),t.on("destroy",c)),c}function Ds(t,e){return e?t+e:t}function ks(t){return"number"==typeof t?t:parseFloat(t)}const Ls=[...ri,Ut,_t],Os=t=>Ls.find(oi(t));function js(t){return"layout"===t?"group":"enter"===t||"new"===t?"new":"exit"===t||"old"===t?"old":"group"}let Bs={},Fs=null;const Is=(t,e)=>{Bs[t]=e},Ws=()=>{Fs||(Fs=document.createElement("style"),Fs.id="motion-view");let t="";for(const e in Bs){const n=Bs[e];t+=`${e} {\n`;for(const[e,i]of Object.entries(n))t+=` ${e}: ${i};\n`;t+="}\n"}Fs.textContent=t,document.head.appendChild(Fs),Bs={}},Us=()=>{Fs&&Fs.parentElement&&Fs.parentElement.removeChild(Fs)};function Ns(t){const e=t.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/);return e?{layer:e[2],type:e[1]}:null}function $s(t){const{effect:e}=t;return!!e&&(e.target===document.documentElement&&e.pseudoElement?.startsWith("::view-transition"))}const zs=["layout","enter","exit","new","old"];function Xs(t){const{update:e,targets:n,options:i}=t;if(!document.startViewTransition)return new Promise(async t=>{await e(),t(new Gn([]))});(function(t,e){return e.has(t)&&Object.keys(e.get(t)).length>0})("root",n)||Is(":root",{"view-transition-name":"none"}),Is("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)",{"animation-timing-function":"linear !important"}),Ws();const s=document.startViewTransition(async()=>{await e()});return s.finished.finally(()=>{Us()}),new Promise(t=>{s.ready.then(()=>{const e=document.getAnimations().filter($s),s=[];n.forEach((t,e)=>{for(const n of zs){if(!t[n])continue;const{keyframes:o,options:r}=t[n];for(let[t,a]of Object.entries(o)){if(!a)continue;const o={...ii(i,t),...ii(r,t)},l=js(n);if("opacity"===t&&!Array.isArray(a)){a=["new"===l?0:1,a]}"function"==typeof o.delay&&(o.delay=o.delay(0,1)),o.duration&&(o.duration=j(o.duration)),o.delay&&(o.delay=j(o.delay));const u=new In({...o,element:document.documentElement,name:t,pseudoElement:`::view-transition-${l}(${e})`,keyframes:a});s.push(u)}}});for(const t of e){if("finished"===t.playState)continue;const{effect:e}=t;if(!(e&&e instanceof KeyframeEffect))continue;const{pseudoElement:o}=e;if(!o)continue;const r=Ns(o);if(!r)continue;const a=n.get(r.layer);if(a)Ys(a,"enter")&&Ys(a,"exit")&&e.getKeyframes().some(t=>t.mixBlendMode)?s.push(new qn(t)):t.cancel();else{const n="group"===r.type?"layout":"";let o={...ii(i,n)};o.duration&&(o.duration=j(o.duration)),o=Fn(o);const a=On(o.ease,o.duration);e.updateTiming({delay:j(o.delay??0),duration:o.duration,easing:a}),s.push(new qn(t))}}t(new Gn(s))})})}function Ys(t,e){return t?.[e]?.keyframes.opacity}let Hs=[],Ks=null;function Gs(){Ks=null;const[t]=Hs;var e;t&&(w(Hs,e=t),Ks=e,Xs(e).then(t=>{e.notifyReady(t),t.finished.finally(Gs)}))}function _s(){for(let t=Hs.length-1;t>=0;t--){const e=Hs[t],{interrupt:n}=e.options;if("immediate"===n){const n=Hs.slice(0,t+1).map(t=>t.update),i=Hs.slice(t+1);e.update=()=>{n.forEach(t=>t())},Hs=[e,...i];break}}Ks&&"immediate"!==Hs[0]?.options.interrupt||Gs()}class qs{constructor(t,e={}){var n;this.currentSubject="root",this.targets=new Map,this.notifyReady=R,this.readyPromise=new Promise(t=>{this.notifyReady=t}),this.update=t,this.options={interrupt:"wait",...e},n=this,Hs.push(n),$i.render(_s)}get(t){return this.currentSubject=t,this}layout(t,e){return this.updateTarget("layout",t,e),this}new(t,e){return this.updateTarget("new",t,e),this}old(t,e){return this.updateTarget("old",t,e),this}enter(t,e){return this.updateTarget("enter",t,e),this}exit(t,e){return this.updateTarget("exit",t,e),this}crossfade(t){return this.updateTarget("enter",{opacity:1},t),this.updateTarget("exit",{opacity:0},t),this}updateTarget(t,e,n={}){const{currentSubject:i,targets:s}=this;s.has(i)||s.set(i,{});s.get(i)[t]={keyframes:e,options:n}}then(t,e){return this.readyPromise.then(t,e)}}const Zs=lt,Js=ot.reduce((t,e)=>(t[e]=t=>ut(t),t),{}),Qs=e.createContext({transformPagePoint:t=>t,isStatic:!1,reducedMotion:"never"});class to extends i.Component{getSnapshotBeforeUpdate(t){const e=this.props.childRef.current;if(e&&t.isPresent&&!this.props.isPresent){const t=e.offsetParent,n=zn(t)&&t.offsetWidth||0,i=this.props.sizeRef.current;i.height=e.offsetHeight||0,i.width=e.offsetWidth||0,i.top=e.offsetTop,i.left=e.offsetLeft,i.right=n-i.width-i.left}return null}componentDidUpdate(){}render(){return this.props.children}}function eo({children:t,isPresent:n,anchorX:s,root:o}){const r=e.useId(),a=e.useRef(null),l=e.useRef({width:0,height:0,top:0,left:0,right:0}),{nonce:u}=e.useContext(Qs);return e.useInsertionEffect(()=>{const{width:t,height:e,top:i,left:c,right:h}=l.current;if(n||!a.current||!t||!e)return;const d="left"===s?`left: ${c}`:`right: ${h}`;a.current.dataset.motionPopId=r;const p=document.createElement("style");u&&(p.nonce=u);const m=o??document.head;return m.appendChild(p),p.sheet&&p.sheet.insertRule(`\n [data-motion-pop-id="${r}"] {\n position: absolute !important;\n width: ${t}px !important;\n height: ${e}px !important;\n ${d}px !important;\n top: ${i}px !important;\n }\n `),()=>{m.removeChild(p),m.contains(p)&&m.removeChild(p)}},[n]),d(to,{isPresent:n,childRef:a,sizeRef:l,children:i.cloneElement(t,{ref:a})})}const no=({children:t,initial:n,isPresent:s,onExitComplete:o,custom:r,presenceAffectsLayout:a,mode:l,anchorX:u,root:c})=>{const h=f(io),p=e.useId();let m=!0,g=e.useMemo(()=>(m=!1,{id:p,initial:n,isPresent:s,custom:r,onExitComplete:t=>{h.set(t,!0);for(const t of h.values())if(!t)return;o&&o()},register:t=>(h.set(t,!1),()=>h.delete(t))}),[s,h,o]);return a&&m&&(g={...g}),e.useMemo(()=>{h.forEach((t,e)=>h.set(e,!1))},[s]),i.useEffect(()=>{!s&&!h.size&&o&&o()},[s]),"popLayout"===l&&(t=d(eo,{isPresent:s,anchorX:u,root:c,children:t})),d(v.Provider,{value:g,children:t})};function io(){return new Map}function so(t=!0){const n=e.useContext(v);if(null===n)return[!0,null];const{isPresent:i,onExitComplete:s,register:o}=n,r=e.useId();e.useEffect(()=>{if(t)return o(r)},[t]);const a=e.useCallback(()=>t&&s&&s(r),[r,s,t]);return!i&&s?[!1,a]:[!0]}const oo=t=>t.key||"";function ro(t){const n=[];return e.Children.forEach(t,t=>{e.isValidElement(t)&&n.push(t)}),n}const ao=e.createContext(null);function lo(t){return t.max-t.min}function uo(t,e,n,i=.5){t.origin=i,t.originPoint=Qt(e.min,e.max,t.origin),t.scale=lo(n)/lo(e),t.translate=Qt(n.min,n.max,t.origin)-t.originPoint,(t.scale>=.9999&&t.scale<=1.0001||isNaN(t.scale))&&(t.scale=1),(t.translate>=-.01&&t.translate<=.01||isNaN(t.translate))&&(t.translate=0)}function co(t,e,n,i){uo(t.x,e.x,n.x,i?i.originX:void 0),uo(t.y,e.y,n.y,i?i.originY:void 0)}function ho(t,e,n){t.min=n.min+e.min,t.max=t.min+lo(e)}function po(t,e,n){t.min=e.min-n.min,t.max=t.min+lo(e)}function mo(t,e,n){po(t.x,e.x,n.x),po(t.y,e.y,n.y)}const fo=t=>!t.isLayoutDirty&&t.willUpdate(!1);function go(){const t=new Set,e=new WeakMap,n=()=>t.forEach(fo);return{add:i=>{t.add(i),e.set(i,i.addEventListener("willUpdate",n))},remove:i=>{t.delete(i);const s=e.get(i);s&&(s(),e.delete(i)),n()},dirty:n}}const yo=t=>null!==t;const vo={type:"spring",stiffness:500,damping:25,restSpeed:10},xo={type:"keyframes",duration:.8},wo={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},To=(t,{keyframes:e})=>e.length>2?xo:pn.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:vo:wo;const Po=(t,e,n,i={},s,o)=>r=>{const a=ii(i,t)||{},l=a.delay||i.delay||0;let{elapsed:u=0}=i;u-=j(l);const c={keyframes:Array.isArray(n)?n:[null,n],ease:"easeOut",velocity:e.getVelocity(),...a,delay:-u,onUpdate:t=>{e.set(t),a.onUpdate&&a.onUpdate(t)},onComplete:()=>{r(),a.onComplete&&a.onComplete()},name:t,motionValue:e,element:o?void 0:s};(function({when:t,delay:e,delayChildren:n,staggerChildren:i,staggerDirection:s,repeat:o,repeatType:r,repeatDelay:a,from:l,elapsed:u,...c}){return!!Object.keys(c).length})(a)||Object.assign(c,To(t,c)),c.duration&&(c.duration=j(c.duration)),c.repeatDelay&&(c.repeatDelay=j(c.repeatDelay)),void 0!==c.from&&(c.keyframes[0]=c.from);let h=!1;if((!1===c.type||0===c.duration&&!c.repeatDelay)&&(c.duration=0,0===c.delay&&(h=!0)),(E.instantAnimations||E.skipAnimations)&&(h=!0,c.duration=0,c.delay=0),c.allowFlatten=!a.type&&!a.ease,h&&!o&&void 0!==e.get()){const t=function(t,{repeat:e,repeatType:n="loop"},i){const s=t.filter(yo),o=e&&"loop"!==n&&e%2==1?0:s.length-1;return o&&void 0!==i?i:s[o]}(c.keyframes,a);if(void 0!==t)return void lt.update(()=>{c.onUpdate(t),c.onComplete()})}return a.isSync?new Je(c):new Kn(c)};function So(t,e,n){const i=Cs(t)?t:ji(t);return i.start(Po("",i,e,n)),i.animation}const bo=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),Eo="framerAppearId",Ao="data-"+bo(Eo);function Mo(t){return t.props[Ao]}const Vo=(t,e)=>t.depth-e.depth;class Co{constructor(){this.children=[],this.isDirty=!1}add(t){x(this.children,t),this.isDirty=!0}remove(t){w(this.children,t),this.isDirty=!0}forEach(t){this.isDirty&&this.children.sort(Vo),this.isDirty=!1,this.children.forEach(t)}}function Ro(t,e){const n=mt.now(),i=({timestamp:s})=>{const o=s-n;o>=e&&(ut(i),t(o-e))};return lt.setup(i,!0),()=>ut(i)}function Do(t){return Cs(t)?t.get():t}const ko=["TopLeft","TopRight","BottomLeft","BottomRight"],Lo=ko.length,Oo=t=>"string"==typeof t?parseFloat(t):t,jo=t=>"number"==typeof t||jt.test(t);function Bo(t,e){return void 0!==t[e]?t[e]:t.borderRadius}const Fo=Wo(0,.5,_),Io=Wo(.5,.95,R);function Wo(t,e,n){return i=>ie?1:n(L(t,e,i))}function Uo(t,e){t.min=e.min,t.max=e.max}function No(t,e){Uo(t.x,e.x),Uo(t.y,e.y)}function $o(t,e){t.translate=e.translate,t.scale=e.scale,t.originPoint=e.originPoint,t.origin=e.origin}function zo(t){return void 0===t||1===t}function Xo({scale:t,scaleX:e,scaleY:n}){return!zo(t)||!zo(e)||!zo(n)}function Yo(t){return Xo(t)||Ho(t)||t.z||t.rotate||t.rotateX||t.rotateY||t.skewX||t.skewY}function Ho(t){return Ko(t.x)||Ko(t.y)}function Ko(t){return t&&"0%"!==t}function Go(t,e,n){return n+e*(t-n)}function _o(t,e,n,i,s){return void 0!==s&&(t=Go(t,s,i)),Go(t,n,i)+e}function qo(t,e=0,n=1,i,s){t.min=_o(t.min,e,n,i,s),t.max=_o(t.max,e,n,i,s)}function Zo(t,{x:e,y:n}){qo(t.x,e.translate,e.scale,e.originPoint),qo(t.y,n.translate,n.scale,n.originPoint)}const Jo=.999999999999,Qo=1.0000000000001;function tr(t,e){t.min=t.min+e,t.max=t.max+e}function er(t,e,n,i,s=.5){qo(t,e,n,Qt(t.min,t.max,s),i)}function nr(t,e){er(t.x,e.x,e.scaleX,e.scale,e.originX),er(t.y,e.y,e.scaleY,e.scale,e.originY)}function ir(t,e,n,i,s){return t=Go(t-=e,1/n,i),void 0!==s&&(t=Go(t,1/s,i)),t}function sr(t,e,[n,i,s],o,r){!function(t,e=0,n=1,i=.5,s,o=t,r=t){Ot.test(e)&&(e=parseFloat(e),e=Qt(r.min,r.max,e/100)-r.min);if("number"!=typeof e)return;let a=Qt(o.min,o.max,i);t===o&&(a-=e),t.min=ir(t.min,e,n,a,s),t.max=ir(t.max,e,n,a,s)}(t,e[n],e[i],e[s],e.scale,o,r)}const or=["x","scaleX","originX"],rr=["y","scaleY","originY"];function ar(t,e,n,i){sr(t.x,e,or,n?n.x:void 0,i?i.x:void 0),sr(t.y,e,rr,n?n.y:void 0,i?i.y:void 0)}const lr=()=>({x:{min:0,max:0},y:{min:0,max:0}});function ur(t){return 0===t.translate&&1===t.scale}function cr(t){return ur(t.x)&&ur(t.y)}function hr(t,e){return t.min===e.min&&t.max===e.max}function dr(t,e){return Math.round(t.min)===Math.round(e.min)&&Math.round(t.max)===Math.round(e.max)}function pr(t,e){return dr(t.x,e.x)&&dr(t.y,e.y)}function mr(t){return lo(t.x)/lo(t.y)}function fr(t,e){return t.translate===e.translate&&t.scale===e.scale&&t.originPoint===e.originPoint}class gr{constructor(){this.members=[]}add(t){x(this.members,t),t.scheduleRender()}remove(t){if(w(this.members,t),t===this.prevLead&&(this.prevLead=void 0),t===this.lead){const t=this.members[this.members.length-1];t&&this.promote(t)}}relegate(t){const e=this.members.findIndex(e=>t===e);if(0===e)return!1;let n;for(let t=e;t>=0;t--){const e=this.members[t];if(!1!==e.isPresent){n=e;break}}return!!n&&(this.promote(n),!0)}promote(t,e){const n=this.lead;if(t!==n&&(this.prevLead=n,this.lead=t,t.show(),n)){n.instance&&n.scheduleRender(),t.scheduleRender(),t.resumeFrom=n,e&&(t.resumeFrom.preserveOpacity=!0),n.snapshot&&(t.snapshot=n.snapshot,t.snapshot.latestValues=n.animationValues||n.latestValues),t.root&&t.root.isUpdating&&(t.isLayoutDirty=!0);const{crossfade:i}=t.options;!1===i&&n.hide()}}exitAnimationComplete(){this.members.forEach(t=>{const{options:e,resumingFrom:n}=t;e.onExitComplete&&e.onExitComplete(),n&&n.options.onExitComplete&&n.options.onExitComplete()})}scheduleRender(){this.members.forEach(t=>{t.instance&&t.scheduleRender(!1)})}removeLeadSnapshot(){this.lead&&this.lead.snapshot&&(this.lead.snapshot=void 0)}}const yr={};function vr(t){for(const e in t)yr[e]=t[e],yt(e)&&(yr[e].isCSSVariable=!0)}function xr(t){return[t("x"),t("y")]}const wr={hasAnimatedSinceResize:!0,hasEverUpdated:!1},Tr={nodes:0,calculatedTargetDeltas:0,calculatedProjections:0},Pr=["","X","Y","Z"];let Sr=0;function br(t,e,n,i){const{latestValues:s}=e;s[t]&&(n[t]=s[t],e.setStaticValue(t,0),i&&(i[t]=0))}function Er(t){if(t.hasCheckedOptimisedAppear=!0,t.root===t)return;const{visualElement:e}=t.options;if(!e)return;const n=Mo(e);if(window.MotionHasOptimisedAnimation(n,"transform")){const{layout:e,layoutId:i}=t.options;window.MotionCancelOptimisedAnimation(n,"transform",lt,!(e||i))}const{parent:i}=t;i&&!i.hasCheckedOptimisedAppear&&Er(i)}function Ar({attachResizeListener:t,defaultParent:e,measureScroll:n,checkIsScrollRoot:i,resetTransform:s}){return class{constructor(t={},n=e?.()){this.id=Sr++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,rt.value&&(Tr.nodes=Tr.calculatedTargetDeltas=Tr.calculatedProjections=0),this.nodes.forEach(Cr),this.nodes.forEach(Br),this.nodes.forEach(Fr),this.nodes.forEach(Rr),rt.addProjectionMetrics&&rt.addProjectionMetrics(Tr)},this.resolvedRelativeTargetAt=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=t,this.root=n?n.root||n:this,this.path=n?[...n.path,n]:[],this.parent=n,this.depth=n?n.depth+1:0;for(let t=0;tthis.root.updateBlockedByResize=!1;lt.read(()=>{i=window.innerWidth}),t(e,()=>{const t=window.innerWidth;t!==i&&(i=t,this.root.updateBlockedByResize=!0,n&&n(),n=Ro(s,250),wr.hasAnimatedSinceResize&&(wr.hasAnimatedSinceResize=!1,this.nodes.forEach(jr)))})}n&&this.root.registerSharedNode(n,this),!1!==this.options.animate&&s&&(n||i)&&this.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e,hasRelativeLayoutChanged:n,layout:i})=>{if(this.isTreeAnimationBlocked())return this.target=void 0,void(this.relativeTarget=void 0);const o=this.options.transition||s.getDefaultTransition()||zr,{onLayoutAnimationStart:r,onLayoutAnimationComplete:a}=s.getProps(),l=!this.targetLayout||!pr(this.targetLayout,i),u=!e&&n;if(this.options.layoutRoot||this.resumeFrom||u||e&&(l||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const e={...ii(o,"layout"),onPlay:r,onComplete:a};(s.shouldReduceMotion||this.options.layoutRoot)&&(e.delay=0,e.type=!1),this.startAnimation(e),this.setAnimationOrigin(t,u)}else e||jr(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=i})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const t=this.getStack();t&&t.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),ut(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(Ir),this.animationId++)}getTransformTemplate(){const{visualElement:t}=this.options;return t&&t.getProps().transformTemplate}willUpdate(t=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked())return void(this.options.onExitComplete&&this.options.onExitComplete());if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&Er(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let t=0;t{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){!this.snapshot&&this.instance&&(this.snapshot=this.measure(),!this.snapshot||lo(this.snapshot.measuredBox.x)||lo(this.snapshot.measuredBox.y)||(this.snapshot=void 0))}updateLayout(){if(!this.instance)return;if(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead()||this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let t=0;tJo&&(e.x=1),e.yJo&&(e.y=1)}(this.layoutCorrected,this.treeScale,this.path,e),!t.layout||t.target||1===this.treeScale.x&&1===this.treeScale.y||(t.target=t.layout.layoutBox,t.targetWithTransforms={x:{min:0,max:0},y:{min:0,max:0}});const{target:a}=t;a?(this.projectionDelta&&this.prevProjectionDelta?($o(this.prevProjectionDelta.x,this.projectionDelta.x),$o(this.prevProjectionDelta.y,this.projectionDelta.y)):this.createProjectionDeltas(),co(this.projectionDelta,this.layoutCorrected,a,this.latestValues),this.treeScale.x===o&&this.treeScale.y===r&&fr(this.projectionDelta.x,this.prevProjectionDelta.x)&&fr(this.projectionDelta.y,this.prevProjectionDelta.y)||(this.hasProjected=!0,this.scheduleRender(),this.notifyListeners("projectionUpdate",a)),rt.value&&Tr.calculatedProjections++):this.prevProjectionDelta&&(this.createProjectionDeltas(),this.scheduleRender())}hide(){this.isVisible=!1}show(){this.isVisible=!0}scheduleRender(t=!0){if(this.options.visualElement?.scheduleRender(),t){const t=this.getStack();t&&t.scheduleRender()}this.resumingFrom&&!this.resumingFrom.instance&&(this.resumingFrom=void 0)}createProjectionDeltas(){this.prevProjectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDeltaWithTransform={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}}}setAnimationOrigin(t,e=!1){const n=this.snapshot,i=n?n.latestValues:{},s={...this.latestValues},o={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};this.relativeParent&&this.relativeParent.options.layoutRoot||(this.relativeTarget=this.relativeTargetOrigin=void 0),this.attemptToResolveRelativeTarget=!e;const r={x:{min:0,max:0},y:{min:0,max:0}},a=(n?n.source:void 0)!==(this.layout?this.layout.source:void 0),l=this.getStack(),u=!l||l.members.length<=1,c=Boolean(a&&!u&&!0===this.options.crossfade&&!this.path.some($r));let h;this.animationProgress=0,this.mixTargetDelta=e=>{const n=e/1e3;var l,d;Ur(o.x,t.x,n),Ur(o.y,t.y,n),this.setTargetDelta(o),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(mo(r,this.layout.layoutBox,this.relativeParent.layout.layoutBox),function(t,e,n,i){Nr(t.x,e.x,n.x,i),Nr(t.y,e.y,n.y,i)}(this.relativeTarget,this.relativeTargetOrigin,r,n),h&&(l=this.relativeTarget,d=h,hr(l.x,d.x)&&hr(l.y,d.y))&&(this.isProjectionDirty=!1),h||(h={x:{min:0,max:0},y:{min:0,max:0}}),No(h,this.relativeTarget)),a&&(this.animationValues=s,function(t,e,n,i,s,o){s?(t.opacity=Qt(0,n.opacity??1,Fo(i)),t.opacityExit=Qt(e.opacity??1,0,Io(i))):o&&(t.opacity=Qt(e.opacity??1,n.opacity??1,i));for(let s=0;s{wr.hasAnimatedSinceResize=!0,ft.layout++,this.motionValue||(this.motionValue=ji(0)),this.currentAnimation=So(this.motionValue,[0,1e3],{...t,velocity:0,isSync:!0,onUpdate:e=>{this.mixTargetDelta(e),t.onUpdate&&t.onUpdate(e)},onStop:()=>{ft.layout--},onComplete:()=>{ft.layout--,t.onComplete&&t.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const t=this.getStack();t&&t.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners("animationComplete")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(1e3),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const t=this.getLead();let{targetWithTransforms:e,target:n,layout:i,latestValues:s}=t;if(e&&n&&i){if(this!==t&&this.layout&&i&&Kr(this.options.animationType,this.layout.layoutBox,i.layoutBox)){n=this.target||{x:{min:0,max:0},y:{min:0,max:0}};const e=lo(this.layout.layoutBox.x);n.x.min=t.target.x.min,n.x.max=n.x.min+e;const i=lo(this.layout.layoutBox.y);n.y.min=t.target.y.min,n.y.max=n.y.min+i}No(e,n),nr(e,s),co(this.projectionDeltaWithTransform,this.layoutCorrected,e,s)}}registerSharedNode(t,e){this.sharedNodes.has(t)||this.sharedNodes.set(t,new gr);this.sharedNodes.get(t).add(e);const n=e.options.initialPromotionConfig;e.promote({transition:n?n.transition:void 0,preserveFollowOpacity:n&&n.shouldPreserveFollowOpacity?n.shouldPreserveFollowOpacity(e):void 0})}isLead(){const t=this.getStack();return!t||t.lead===this}getLead(){const{layoutId:t}=this.options;return t&&this.getStack()?.lead||this}getPrevLead(){const{layoutId:t}=this.options;return t?this.getStack()?.prevLead:void 0}getStack(){const{layoutId:t}=this.options;if(t)return this.root.sharedNodes.get(t)}promote({needsReset:t,transition:e,preserveFollowOpacity:n}={}){const i=this.getStack();i&&i.promote(this,n),t&&(this.projectionDelta=void 0,this.needsReset=!0),e&&this.setOptions({transition:e})}relegate(){const t=this.getStack();return!!t&&t.relegate(this)}resetSkewAndRotation(){const{visualElement:t}=this.options;if(!t)return;let e=!1;const{latestValues:n}=t;if((n.z||n.rotate||n.rotateX||n.rotateY||n.rotateZ||n.skewX||n.skewY)&&(e=!0),!e)return;const i={};n.z&&br("z",t,i,this.animationValues);for(let e=0;et.currentAnimation?.stop()),this.root.nodes.forEach(kr),this.root.sharedNodes.clear()}}}function Mr(t){t.updateLayout()}function Vr(t){const e=t.resumeFrom?.snapshot||t.snapshot;if(t.isLead()&&t.layout&&e&&t.hasListeners("didUpdate")){const{layoutBox:n,measuredBox:i}=t.layout,{animationType:s}=t.options,o=e.source!==t.layout.source;"size"===s?xr(t=>{const i=o?e.measuredBox[t]:e.layoutBox[t],s=lo(i);i.min=n[t].min,i.max=i.min+s}):Kr(s,e.layoutBox,n)&&xr(i=>{const s=o?e.measuredBox[i]:e.layoutBox[i],r=lo(n[i]);s.max=s.min+r,t.relativeTarget&&!t.currentAnimation&&(t.isProjectionDirty=!0,t.relativeTarget[i].max=t.relativeTarget[i].min+r)});const r={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};co(r,n,e.layoutBox);const a={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};o?co(a,t.applyTransform(i,!0),e.measuredBox):co(a,n,e.layoutBox);const l=!cr(r);let u=!1;if(!t.resumeFrom){const i=t.getClosestProjectingParent();if(i&&!i.resumeFrom){const{snapshot:s,layout:o}=i;if(s&&o){const r={x:{min:0,max:0},y:{min:0,max:0}};mo(r,e.layoutBox,s.layoutBox);const a={x:{min:0,max:0},y:{min:0,max:0}};mo(a,n,o.layoutBox),pr(r,a)||(u=!0),i.options.layoutRoot&&(t.relativeTarget=a,t.relativeTargetOrigin=r,t.relativeParent=i)}}}t.notifyListeners("didUpdate",{layout:n,snapshot:e,delta:a,layoutDelta:r,hasLayoutChanged:l,hasRelativeLayoutChanged:u})}else if(t.isLead()){const{onExitComplete:e}=t.options;e&&e()}t.options.transition=void 0}function Cr(t){rt.value&&Tr.nodes++,t.parent&&(t.isProjecting()||(t.isProjectionDirty=t.parent.isProjectionDirty),t.isSharedProjectionDirty||(t.isSharedProjectionDirty=Boolean(t.isProjectionDirty||t.parent.isProjectionDirty||t.parent.isSharedProjectionDirty)),t.isTransformDirty||(t.isTransformDirty=t.parent.isTransformDirty))}function Rr(t){t.isProjectionDirty=t.isSharedProjectionDirty=t.isTransformDirty=!1}function Dr(t){t.clearSnapshot()}function kr(t){t.clearMeasurements()}function Lr(t){t.isLayoutDirty=!1}function Or(t){const{visualElement:e}=t.options;e&&e.getProps().onBeforeLayoutMeasure&&e.notify("BeforeLayoutMeasure"),t.resetTransform()}function jr(t){t.finishAnimation(),t.targetDelta=t.relativeTarget=t.target=void 0,t.isProjectionDirty=!0}function Br(t){t.resolveTargetDelta()}function Fr(t){t.calcProjection()}function Ir(t){t.resetSkewAndRotation()}function Wr(t){t.removeLeadSnapshot()}function Ur(t,e,n){t.translate=Qt(e.translate,0,n),t.scale=Qt(e.scale,1,n),t.origin=e.origin,t.originPoint=e.originPoint}function Nr(t,e,n,i){t.min=Qt(e.min,n.min,i),t.max=Qt(e.max,n.max,i)}function $r(t){return t.animationValues&&void 0!==t.animationValues.opacityExit}const zr={duration:.45,ease:[.4,0,.1,1]},Xr=t=>"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(t),Yr=Xr("applewebkit/")&&!Xr("chrome/")?Math.round:R;function Hr(t){t.min=Yr(t.min),t.max=Yr(t.max)}function Kr(t,e,n){return"position"===t||"preserve-aspect"===t&&(i=mr(e),s=mr(n),o=.2,!(Math.abs(i-s)<=o));var i,s,o}function Gr(t){return t!==t.root&&t.scroll?.wasRoot}function _r(t,e,n,i={passive:!0}){return t.addEventListener(e,n,i),()=>t.removeEventListener(e,n)}const qr=Ar({attachResizeListener:(t,e)=>_r(t,"resize",e),measureScroll:()=>({x:document.documentElement.scrollLeft||document.body.scrollLeft,y:document.documentElement.scrollTop||document.body.scrollTop}),checkIsScrollRoot:()=>!0}),Zr={current:void 0},Jr=Ar({measureScroll:t=>({x:t.scrollLeft,y:t.scrollTop}),defaultParent:()=>{if(!Zr.current){const t=new qr({});t.mount(window),t.setOptions({layoutScroll:!0}),Zr.current=t}return Zr.current},resetTransform:(t,e)=>{t.style.transform=void 0!==e?e:"none"},checkIsScrollRoot:t=>Boolean("fixed"===window.getComputedStyle(t).position)});function Qr(t,e){return e.max===e.min?0:t/(e.max-e.min)*100}const ta={correct:(t,e)=>{if(!e.target)return t;if("string"==typeof t){if(!jt.test(t))return t;t=parseFloat(t)}return`${Qr(t,e.target.x)}% ${Qr(t,e.target.y)}%`}},ea={correct:(t,{treeScale:e,projectionDelta:n})=>{const i=t,s=_t.parse(t);if(s.length>5)return i;const o=_t.createTransformer(t),r="number"!=typeof s[0]?1:0,a=n.x.scale*e.x,l=n.y.scale*e.y;s[0+r]/=a,s[1+r]/=l;const u=Qt(a,l,.5);return"number"==typeof s[2+r]&&(s[2+r]/=u),"number"==typeof s[3+r]&&(s[3+r]/=u),o(s)}};function na({top:t,left:e,right:n,bottom:i}){return{x:{min:e,max:n},y:{min:t,max:i}}}function ia(t,e){return na(function(t,e){if(!e)return t;const n=e({x:t.left,y:t.top}),i=e({x:t.right,y:t.bottom});return{top:n.y,left:n.x,bottom:i.y,right:i.x}}(t.getBoundingClientRect(),e))}const sa={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},oa={};for(const t in sa)oa[t]={isEnabled:e=>sa[t].some(t=>!!e[t])};const ra={current:null},aa={current:!1};function la(){if(aa.current=!0,g)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>ra.current=t.matches;t.addEventListener("change",e),e()}else ra.current=!1}const ua=new WeakMap;function ca(t){return null!==t&&"object"==typeof t&&"function"==typeof t.start}function ha(t){return"string"==typeof t||Array.isArray(t)}const da=["animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"],pa=["initial",...da];function ma(t){return ca(t.animate)||pa.some(e=>ha(t[e]))}function fa(t){return Boolean(ma(t)||t.variants)}function ga(t){const e=[{},{}];return t?.values.forEach((t,n)=>{e[0][n]=t.get(),e[1][n]=t.getVelocity()}),e}function ya(t,e,n,i){if("function"==typeof e){const[s,o]=ga(i);e=e(void 0!==n?n:t.custom,s,o)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[s,o]=ga(i);e=e(void 0!==n?n:t.custom,s,o)}return e}const va=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class xa{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:i,blockInitialAnimation:s,visualState:o},r={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=En,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=mt.now();this.renderScheduledAtthis.bindToMotionValue(e,t)),aa.current||la(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||ra.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){this.projection&&this.projection.unmount(),ut(this.notifyUpdate),ut(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=pn.has(t);n&&this.onBindTransform&&this.onBindTransform();const i=e.on("change",e=>{this.latestValues[t]=e,this.props.onUpdate&<.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0)}),s=e.on("renderRequest",this.scheduleRender);let o;window.MotionCheckAppearSync&&(o=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{i(),s(),o&&o(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in oa){const e=oa[t];if(!e)continue;const{isEnabled:n,Feature:i}=e;if(!this.features[t]&&i&&n(this.props)&&(this.features[t]=new i(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=ji(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){let n=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];return null!=n&&("string"==typeof n&&(A(n)||V(n))?n=parseFloat(n):!Os(n)&&_t.test(e)&&(n=vi(t,e)),this.setBaseTarget(t,Cs(n)?n.get():n)),Cs(n)?n.get():n}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){const{initial:e}=this.props;let n;if("string"==typeof e||"object"==typeof e){const i=ya(this.props,e,this.presenceContext?.custom);i&&(n=i[t])}if(e&&void 0!==n)return n;const i=this.getBaseTargetFromProps(this.props,t);return void 0===i||Cs(i)?void 0!==this.initialValues[t]&&void 0===n?void 0:this.baseTarget[t]:i}on(t,e){return this.events[t]||(this.events[t]=new O),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class wa extends xa{constructor(){super(...arguments),this.KeyframeResolver=wi}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;Cs(t)&&(this.childSubscription=t.on("change",t=>{this.current&&(this.current.textContent=`${t}`)}))}}const Ta={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},Pa=dn.length;function Sa(t,e,n){let i="",s=!0;for(let o=0;o(t.current=!0,()=>{t.current=!1}),[]),t}(),[n,i]=e.useState(0),s=e.useCallback(()=>{t.current&&i(n+1)},[n]);return[e.useCallback(()=>lt.postRender(s),[s]),n]}const Ra=t=>!0===t,Da=({children:t,id:n,inherit:i=!0})=>{const s=e.useContext(m),o=e.useContext(ao),[r,a]=Ca(),l=e.useRef(null),u=s.id||o;null===l.current&&((t=>Ra(!0===t)||"id"===t)(i)&&u&&(n=n?u+"-"+n:u),l.current={id:n,group:Ra(i)&&s.group||go()});const c=e.useMemo(()=>({...l.current,forceRender:r}),[a]);return d(m.Provider,{value:c,children:t})},ka=e.createContext({strict:!1});function La(t){for(const e in t)oa[e]={...oa[e],...t[e]}}function Oa(t){return"function"==typeof t}const ja=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","ignoreStrict","viewport"]);function Ba(t){return t.startsWith("while")||t.startsWith("drag")&&"draggable"!==t||t.startsWith("layout")||t.startsWith("onTap")||t.startsWith("onPan")||t.startsWith("onLayout")||ja.has(t)}let Fa=t=>!Ba(t);function Ia(t){"function"==typeof t&&(Fa=e=>e.startsWith("on")?!Ba(e):t(e))}try{Ia(require("@emotion/is-prop-valid").default)}catch{}function Wa(t,e,n){const i={};for(const s in t)"values"===s&&"object"==typeof t.values||(Fa(s)||!0===n&&Ba(s)||!e&&!Ba(s)||t.draggable&&s.startsWith("onDrag"))&&(i[s]=t[s]);return i}const Ua=e.createContext(null);function Na(t){if("undefined"==typeof Proxy)return t;const e=new Map;return new Proxy((...e)=>t(...e),{get:(n,i)=>"create"===i?t:(e.has(i)||e.set(i,t(i)),e.get(i))})}function $a(t,e,n){const i=t.getProps();return ya(i,e,void 0!==n?n:i.custom,t)}const za=t=>Array.isArray(t);function Xa(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,ji(n))}function Ya(t){return za(t)?t[t.length-1]||0:t}function Ha(t,e){const n=$a(t,e);let{transitionEnd:i={},transition:s={},...o}=n||{};o={...o,...i};for(const e in o){Xa(t,e,Ya(o[e]))}}function Ka(t,e){const n=t.getValue("willChange");if(i=n,Boolean(Cs(i)&&i.add))return n.add(e);if(!n&&E.WillChange){const n=new E.WillChange("auto");t.addValue("willChange",n),n.add(e)}var i}function Ga({protectedKeys:t,needsAnimating:e},n){const i=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,i}function _a(t,e,{delay:n=0,transitionOverride:i,type:s}={}){let{transition:o=t.getDefaultTransition(),transitionEnd:r,...a}=e;i&&(o=i);const l=[],u=s&&t.animationState&&t.animationState.getState()[s];for(const e in a){const i=t.getValue(e,t.latestValues[e]??null),s=a[e];if(void 0===s||u&&Ga(u,e))continue;const r={delay:n,...ii(o||{},e)},c=i.get();if(void 0!==c&&!i.isAnimating&&!Array.isArray(s)&&s===c&&!r.velocity)continue;let h=!1;if(window.MotionHandoffAnimation){const n=Mo(t);if(n){const t=window.MotionHandoffAnimation(n,e,lt);null!==t&&(r.startTime=t,h=!0)}}Ka(t,e),i.start(Po(e,i,s,t.shouldReduceMotion&&si.has(e)?{type:!1}:r,t,h));const d=i.animation;d&&l.push(d)}return r&&Promise.all(l).then(()=>{lt.update(()=>{r&&Ha(t,r)})}),l}function qa(t,e,n={}){const i=$a(t,e,"exit"===n.type?t.presenceContext?.custom:void 0);let{transition:s=t.getDefaultTransition()||{}}=i||{};n.transitionOverride&&(s=n.transitionOverride);const o=i?()=>Promise.all(_a(t,i,n)):()=>Promise.resolve(),r=t.variantChildren&&t.variantChildren.size?(i=0)=>{const{delayChildren:o=0,staggerChildren:r,staggerDirection:a}=s;return function(t,e,n=0,i=0,s=0,o=1,r){const a=[],l=t.variantChildren.size,u=(l-1)*s,c="function"==typeof i,h=c?t=>i(t,l):1===o?(t=0)=>t*s:(t=0)=>u-t*s;return Array.from(t.variantChildren).sort(Za).forEach((t,s)=>{t.notify("AnimationStart",e),a.push(qa(t,e,{...r,delay:n+(c?0:i)+h(s)}).then(()=>t.notify("AnimationComplete",e)))}),Promise.all(a)}(t,e,i,o,r,a,n)}:()=>Promise.resolve(),{when:a}=s;if(a){const[t,e]="beforeChildren"===a?[o,r]:[r,o];return t().then(()=>e())}return Promise.all([o(),r(n.delay)])}function Za(t,e){return t.sortNodePosition(e)}function Ja(t,e,n={}){let i;if(t.notify("AnimationStart",e),Array.isArray(e)){const s=e.map(e=>qa(t,e,n));i=Promise.all(s)}else if("string"==typeof e)i=qa(t,e,n);else{const s="function"==typeof e?$a(t,e,n.custom):e;i=Promise.all(_a(t,s,n))}return i.then(()=>{t.notify("AnimationComplete",e)})}function Qa(t,e){if(!Array.isArray(e))return!1;const n=e.length;if(n!==t.length)return!1;for(let i=0;iPromise.all(e.map(({animation:e,options:n})=>Ja(t,e,n)))}(t),n=al(),i=!0;const s=e=>(n,i)=>{const s=$a(t,i,"exit"===e?t.presenceContext?.custom:void 0);if(s){const{transition:t,transitionEnd:e,...i}=s;n={...n,...i,...e}}return n};function o(o){const{props:r}=t,a=el(t.parent)||{},l=[],u=new Set;let c={},h=1/0;for(let e=0;eh&&f,w=!1;const T=Array.isArray(m)?m:[m];let P=T.reduce(s(d),{});!1===g&&(P={});const{prevResolvedValues:S={}}=p,b={...S,...P},E=e=>{x=!0,u.has(e)&&(w=!0,u.delete(e)),p.needsAnimating[e]=!0;const n=t.getValue(e);n&&(n.liveStyle=!1)};for(const t in b){const e=P[t],n=S[t];if(c.hasOwnProperty(t))continue;let i=!1;i=za(e)&&za(n)?!Qa(e,n):e!==n,i?null!=e?E(t):u.add(t):void 0!==e&&u.has(t)?E(t):p.protectedKeys[t]=!0}p.prevProp=m,p.prevResolvedValues=P,p.isActive&&(c={...c,...P}),i&&t.blockInitialAnimation&&(x=!1);x&&(!(y&&v)||w)&&l.push(...T.map(t=>({animation:t,options:{type:d}})))}if(u.size){const e={};if("boolean"!=typeof r.initial){const n=$a(t,Array.isArray(r.initial)?r.initial[0]:r.initial);n&&n.transition&&(e.transition=n.transition)}u.forEach(n=>{const i=t.getBaseTarget(n),s=t.getValue(n);s&&(s.liveStyle=!0),e[n]=i??null}),l.push({animation:e})}let d=Boolean(l.length);return!i||!1!==r.initial&&r.initial!==r.animate||t.manuallyAnimateOnMount||(d=!1),i=!1,d?e(l):Promise.resolve()}return{animateChanges:o,setActive:function(e,i){if(n[e].isActive===i)return Promise.resolve();t.variantChildren?.forEach(t=>t.animationState?.setActive(e,i)),n[e].isActive=i;const s=o(e);for(const t in n)n[t].protectedKeys={};return s},setAnimateFunction:function(n){e=n(t)},getState:()=>n,reset:()=>{n=al(),i=!0}}}function ol(t,e){return"string"==typeof e?e!==t:!!Array.isArray(e)&&!Qa(e,t)}function rl(t=!1){return{isActive:t,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function al(){return{animate:rl(!0),whileInView:rl(),whileHover:rl(),whileTap:rl(),whileDrag:rl(),whileFocus:rl(),exit:rl()}}class ll{constructor(t){this.isMounted=!1,this.node=t}update(){}}let ul=0;const cl={animation:{Feature:class extends ll{constructor(t){super(t),t.animationState||(t.animationState=sl(t))}updateAnimationControlsSubscription(){const{animate:t}=this.node.getProps();ca(t)&&(this.unmountControls=t.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:t}=this.node.getProps(),{animate:e}=this.node.prevProps||{};t!==e&&this.updateAnimationControlsSubscription()}unmount(){this.node.animationState.reset(),this.unmountControls?.()}}},exit:{Feature:class extends ll{constructor(){super(...arguments),this.id=ul++}update(){if(!this.node.presenceContext)return;const{isPresent:t,onExitComplete:e}=this.node.presenceContext,{isPresent:n}=this.node.prevPresenceContext||{};if(!this.node.animationState||t===n)return;const i=this.node.animationState.setActive("exit",!t);e&&!t&&i.then(()=>{e(this.id)})}mount(){const{register:t,onExitComplete:e}=this.node.presenceContext||{};e&&e(this.id),t&&(this.unmount=t(this.id))}unmount(){}}}};function hl(t){return{point:{x:t.pageX,y:t.pageY}}}const dl=t=>e=>Zi(e)&&t(e,hl(e));function pl(t,e,n,i){return _r(t,e,dl(n),i)}const ml=({current:t})=>t?t.ownerDocument.defaultView:null;function fl(t){return t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"current")}const gl=(t,e)=>Math.abs(t-e);function yl(t,e){const n=gl(t.x,e.x),i=gl(t.y,e.y);return Math.sqrt(n**2+i**2)}class vl{constructor(t,e,{transformPagePoint:n,contextWindow:i=window,dragSnapToOrigin:s=!1,distanceThreshold:o=3}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.updatePoint=()=>{if(!this.lastMoveEvent||!this.lastMoveEventInfo)return;const t=Tl(this.lastMoveEventInfo,this.history),e=null!==this.startEvent,n=yl(t.offset,{x:0,y:0})>=this.distanceThreshold;if(!e&&!n)return;const{point:i}=t,{timestamp:s}=ct;this.history.push({...i,timestamp:s});const{onStart:o,onMove:r}=this.handlers;e||(o&&o(this.lastMoveEvent,t),this.startEvent=this.lastMoveEvent),r&&r(this.lastMoveEvent,t)},this.handlePointerMove=(t,e)=>{this.lastMoveEvent=t,this.lastMoveEventInfo=xl(e,this.transformPagePoint),lt.update(this.updatePoint,!0)},this.handlePointerUp=(t,e)=>{this.end();const{onEnd:n,onSessionEnd:i,resumeAnimation:s}=this.handlers;if(this.dragSnapToOrigin&&s&&s(),!this.lastMoveEvent||!this.lastMoveEventInfo)return;const o=Tl("pointercancel"===t.type?this.lastMoveEventInfo:xl(e,this.transformPagePoint),this.history);this.startEvent&&n&&n(t,o),i&&i(t,o)},!Zi(t))return;this.dragSnapToOrigin=s,this.handlers=e,this.transformPagePoint=n,this.distanceThreshold=o,this.contextWindow=i||window;const r=xl(hl(t),this.transformPagePoint),{point:a}=r,{timestamp:l}=ct;this.history=[{...a,timestamp:l}];const{onSessionStart:u}=e;u&&u(t,Tl(r,this.history)),this.removeListeners=k(pl(this.contextWindow,"pointermove",this.handlePointerMove),pl(this.contextWindow,"pointerup",this.handlePointerUp),pl(this.contextWindow,"pointercancel",this.handlePointerUp))}updateHandlers(t){this.handlers=t}end(){this.removeListeners&&this.removeListeners(),ut(this.updatePoint)}}function xl(t,e){return e?{point:e(t.point)}:t}function wl(t,e){return{x:t.x-e.x,y:t.y-e.y}}function Tl({point:t},e){return{point:t,delta:wl(t,Sl(e)),offset:wl(t,Pl(e)),velocity:bl(e,.1)}}function Pl(t){return t[0]}function Sl(t){return t[t.length-1]}function bl(t,e){if(t.length<2)return{x:0,y:0};let n=t.length-1,i=null;const s=Sl(t);for(;n>=0&&(i=t[n],!(s.timestamp-i.timestamp>j(e)));)n--;if(!i)return{x:0,y:0};const o=B(s.timestamp-i.timestamp);if(0===o)return{x:0,y:0};const r={x:(s.x-i.x)/o,y:(s.y-i.y)/o};return r.x===1/0&&(r.x=0),r.y===1/0&&(r.y=0),r}function El(t,e,n){return{min:void 0!==e?t.min+e:void 0,max:void 0!==n?t.max+n-(t.max-t.min):void 0}}function Al(t,e){let n=e.min-t.min,i=e.max-t.max;return e.max-e.min{const{dragSnapToOrigin:n}=this.getProps();n?this.pauseAnimation():this.stopAnimation(),e&&this.snapToCursor(hl(t).point)},onStart:(t,e)=>{const{drag:n,dragPropagation:i,onDragStart:s}=this.getProps();if(n&&!i&&(this.openDragLock&&this.openDragLock(),this.openDragLock=Hi(n),!this.openDragLock))return;this.latestPointerEvent=t,this.latestPanInfo=e,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),xr(t=>{let e=this.getAxisMotionValue(t).get()||0;if(Ot.test(e)){const{projection:n}=this.visualElement;if(n&&n.layout){const i=n.layout.layoutBox[t];if(i){e=lo(i)*(parseFloat(e)/100)}}}this.originPoint[t]=e}),s&<.postRender(()=>s(t,e)),Ka(this.visualElement,"transform");const{animationState:o}=this.visualElement;o&&o.setActive("whileDrag",!0)},onMove:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e;const{dragPropagation:n,dragDirectionLock:i,onDirectionLock:s,onDrag:o}=this.getProps();if(!n&&!this.openDragLock)return;const{offset:r}=e;if(i&&null===this.currentDirection)return this.currentDirection=function(t,e=10){let n=null;Math.abs(t.y)>e?n="y":Math.abs(t.x)>e&&(n="x");return n}(r),void(null!==this.currentDirection&&s&&s(this.currentDirection));this.updateAxis("x",e.point,r),this.updateAxis("y",e.point,r),this.visualElement.render(),o&&o(t,e)},onSessionEnd:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e,this.stop(t,e),this.latestPointerEvent=null,this.latestPanInfo=null},resumeAnimation:()=>xr(t=>"paused"===this.getAnimationState(t)&&this.getAxisMotionValue(t).animation?.play())},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:s,distanceThreshold:n,contextWindow:ml(this.visualElement)})}stop(t,e){const n=t||this.latestPointerEvent,i=e||this.latestPanInfo,s=this.isDragging;if(this.cancel(),!s||!i||!n)return;const{velocity:o}=i;this.startAnimation(o);const{onDragEnd:r}=this.getProps();r&<.postRender(()=>r(n,i))}cancel(){this.isDragging=!1;const{projection:t,animationState:e}=this.visualElement;t&&(t.isAnimationBlocked=!1),this.panSession&&this.panSession.end(),this.panSession=void 0;const{dragPropagation:n}=this.getProps();!n&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),e&&e.setActive("whileDrag",!1)}updateAxis(t,e,n){const{drag:i}=this.getProps();if(!n||!kl(t,i,this.currentDirection))return;const s=this.getAxisMotionValue(t);let o=this.originPoint[t]+n[t];this.constraints&&this.constraints[t]&&(o=function(t,{min:e,max:n},i){return void 0!==e&&tn&&(t=i?Qt(n,t,i.max):Math.min(t,n)),t}(o,this.constraints[t],this.elastic[t])),s.set(o)}resolveConstraints(){const{dragConstraints:t,dragElastic:e}=this.getProps(),n=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):this.visualElement.projection?.layout,i=this.constraints;t&&fl(t)?this.constraints||(this.constraints=this.resolveRefConstraints()):this.constraints=!(!t||!n)&&function(t,{top:e,left:n,bottom:i,right:s}){return{x:El(t.x,n,s),y:El(t.y,e,i)}}(n.layoutBox,t),this.elastic=function(t=Ml){return!1===t?t=0:!0===t&&(t=Ml),{x:Vl(t,"left","right"),y:Vl(t,"top","bottom")}}(e),i!==this.constraints&&n&&this.constraints&&!this.hasMutatedConstraints&&xr(t=>{!1!==this.constraints&&this.getAxisMotionValue(t)&&(this.constraints[t]=function(t,e){const n={};return void 0!==e.min&&(n.min=e.min-t.min),void 0!==e.max&&(n.max=e.max-t.min),n}(n.layoutBox[t],this.constraints[t]))})}resolveRefConstraints(){const{dragConstraints:t,onMeasureDragConstraints:e}=this.getProps();if(!t||!fl(t))return!1;const n=t.current,{projection:i}=this.visualElement;if(!i||!i.layout)return!1;const s=function(t,e,n){const i=ia(t,n),{scroll:s}=e;return s&&(tr(i.x,s.offset.x),tr(i.y,s.offset.y)),i}(n,i.root,this.visualElement.getTransformPagePoint());let o=function(t,e){return{x:Al(t.x,e.x),y:Al(t.y,e.y)}}(i.layout.layoutBox,s);if(e){const t=e(function({x:t,y:e}){return{top:e.min,right:t.max,bottom:e.max,left:t.min}}(o));this.hasMutatedConstraints=!!t,t&&(o=na(t))}return o}startAnimation(t){const{drag:e,dragMomentum:n,dragElastic:i,dragTransition:s,dragSnapToOrigin:o,onDragTransitionEnd:r}=this.getProps(),a=this.constraints||{},l=xr(r=>{if(!kl(r,e,this.currentDirection))return;let l=a&&a[r]||{};o&&(l={min:0,max:0});const u=i?200:1e6,c=i?40:1e7,h={type:"inertia",velocity:n?t[r]:0,bounceStiffness:u,bounceDamping:c,timeConstant:750,restDelta:1,restSpeed:10,...s,...l};return this.startAxisValueAnimation(r,h)});return Promise.all(l).then(r)}startAxisValueAnimation(t,e){const n=this.getAxisMotionValue(t);return Ka(this.visualElement,t),n.start(Po(t,n,0,e,this.visualElement,!1))}stopAnimation(){xr(t=>this.getAxisMotionValue(t).stop())}pauseAnimation(){xr(t=>this.getAxisMotionValue(t).animation?.pause())}getAnimationState(t){return this.getAxisMotionValue(t).animation?.state}getAxisMotionValue(t){const e=`_drag${t.toUpperCase()}`,n=this.visualElement.getProps(),i=n[e];return i||this.visualElement.getValue(t,(n.initial?n.initial[t]:void 0)||0)}snapToCursor(t){xr(e=>{const{drag:n}=this.getProps();if(!kl(e,n,this.currentDirection))return;const{projection:i}=this.visualElement,s=this.getAxisMotionValue(e);if(i&&i.layout){const{min:n,max:o}=i.layout.layoutBox[e];s.set(t[e]-Qt(n,o,.5))}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:t,dragConstraints:e}=this.getProps(),{projection:n}=this.visualElement;if(!fl(e)||!n||!this.constraints)return;this.stopAnimation();const i={x:0,y:0};xr(t=>{const e=this.getAxisMotionValue(t);if(e&&!1!==this.constraints){const n=e.get();i[t]=function(t,e){let n=.5;const i=lo(t),s=lo(e);return s>i?n=L(e.min,e.max-i,t.min):i>s&&(n=L(t.min,t.max-s,e.min)),P(0,1,n)}({min:n,max:n},this.constraints[t])}});const{transformTemplate:s}=this.visualElement.getProps();this.visualElement.current.style.transform=s?s({},""):"none",n.root&&n.root.updateScroll(),n.updateLayout(),this.resolveConstraints(),xr(e=>{if(!kl(e,t,null))return;const n=this.getAxisMotionValue(e),{min:s,max:o}=this.constraints[e];n.set(Qt(s,o,i[e]))})}addListeners(){if(!this.visualElement.current)return;Rl.set(this.visualElement,this);const t=pl(this.visualElement.current,"pointerdown",t=>{const{drag:e,dragListener:n=!0}=this.getProps();e&&n&&this.start(t)}),e=()=>{const{dragConstraints:t}=this.getProps();fl(t)&&t.current&&(this.constraints=this.resolveRefConstraints())},{projection:n}=this.visualElement,i=n.addEventListener("measure",e);n&&!n.layout&&(n.root&&n.root.updateScroll(),n.updateLayout()),lt.read(e);const s=_r(window,"resize",()=>this.scalePositionWithinConstraints()),o=n.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e})=>{this.isDragging&&e&&(xr(e=>{const n=this.getAxisMotionValue(e);n&&(this.originPoint[e]+=t[e].translate,n.set(n.get()+t[e].translate))}),this.visualElement.render())});return()=>{s(),t(),i(),o&&o()}}getProps(){const t=this.visualElement.getProps(),{drag:e=!1,dragDirectionLock:n=!1,dragPropagation:i=!1,dragConstraints:s=!1,dragElastic:o=Ml,dragMomentum:r=!0}=t;return{...t,drag:e,dragDirectionLock:n,dragPropagation:i,dragConstraints:s,dragElastic:o,dragMomentum:r}}}function kl(t,e,n){return!(!0!==e&&e!==t||null!==n&&n!==t)}const Ll=t=>(e,n)=>{t&<.postRender(()=>t(e,n))};const Ol=e.createContext({});let jl=!1;class Bl extends e.Component{componentDidMount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:n,layoutId:i}=this.props,{projection:s}=t;vr(Il),s&&(e.group&&e.group.add(s),n&&n.register&&i&&n.register(s),jl&&s.root.didUpdate(),s.addEventListener("animationComplete",()=>{this.safeToRemove()}),s.setOptions({...s.options,onExitComplete:()=>this.safeToRemove()})),wr.hasEverUpdated=!0}getSnapshotBeforeUpdate(t){const{layoutDependency:e,visualElement:n,drag:i,isPresent:s}=this.props,{projection:o}=n;return o?(o.isPresent=s,jl=!0,i||t.layoutDependency!==e||void 0===e||t.isPresent!==s?o.willUpdate():this.safeToRemove(),t.isPresent!==s&&(s?o.promote():o.relegate()||lt.postRender(()=>{const t=o.getStack();t&&t.members.length||this.safeToRemove()})),null):null}componentDidUpdate(){const{projection:t}=this.props.visualElement;t&&(t.root.didUpdate(),$i.postRender(()=>{!t.currentAnimation&&t.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:n}=this.props,{projection:i}=t;i&&(i.scheduleCheckAfterUnmount(),e&&e.group&&e.group.remove(i),n&&n.deregister&&n.deregister(i))}safeToRemove(){const{safeToRemove:t}=this.props;t&&t()}render(){return null}}function Fl(t){const[n,i]=so(),s=e.useContext(m);return d(Bl,{...t,layoutGroup:s,switchLayoutGroup:e.useContext(Ol),isPresent:n,safeToRemove:i})}const Il={borderRadius:{...ta,applyTo:["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"]},borderTopLeftRadius:ta,borderTopRightRadius:ta,borderBottomLeftRadius:ta,borderBottomRightRadius:ta,boxShadow:ea},Wl={pan:{Feature:class extends ll{constructor(){super(...arguments),this.removePointerDownListener=R}onPointerDown(t){this.session=new vl(t,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:ml(this.node)})}createPanHandlers(){const{onPanSessionStart:t,onPanStart:e,onPan:n,onPanEnd:i}=this.node.getProps();return{onSessionStart:Ll(t),onStart:Ll(e),onMove:n,onEnd:(t,e)=>{delete this.session,i&<.postRender(()=>i(t,e))}}}mount(){this.removePointerDownListener=pl(this.node.current,"pointerdown",t=>this.onPointerDown(t))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}},drag:{Feature:class extends ll{constructor(t){super(t),this.removeGroupControls=R,this.removeListeners=R,this.controls=new Dl(t)}mount(){const{dragControls:t}=this.node.getProps();t&&(this.removeGroupControls=t.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||R}unmount(){this.removeGroupControls(),this.removeListeners()}},ProjectionNode:Jr,MeasureLayout:Fl}};function Ul(t,e,n){const{props:i}=t;t.animationState&&i.whileHover&&t.animationState.setActive("whileHover","Start"===n);const s=i["onHover"+n];s&<.postRender(()=>s(e,hl(e)))}function Nl(t,e,n){const{props:i}=t;if(t.current instanceof HTMLButtonElement&&t.current.disabled)return;t.animationState&&i.whileTap&&t.animationState.setActive("whileTap","Start"===n);const s=i["onTap"+("End"===n?"":n)];s&<.postRender(()=>s(e,hl(e)))}const $l=new WeakMap,zl=new WeakMap,Xl=t=>{const e=$l.get(t.target);e&&e(t)},Yl=t=>{t.forEach(Xl)};function Hl(t,e,n){const i=function({root:t,...e}){const n=t||document;zl.has(n)||zl.set(n,{});const i=zl.get(n),s=JSON.stringify(e);return i[s]||(i[s]=new IntersectionObserver(Yl,{root:t,...e})),i[s]}(e);return $l.set(t,n),i.observe(t),()=>{$l.delete(t),i.unobserve(t)}}const Kl={some:0,all:1};const Gl={inView:{Feature:class extends ll{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){this.unmount();const{viewport:t={}}=this.node.getProps(),{root:e,margin:n,amount:i="some",once:s}=t,o={root:e?e.current:void 0,rootMargin:n,threshold:"number"==typeof i?i:Kl[i]};return Hl(this.node.current,o,t=>{const{isIntersecting:e}=t;if(this.isInView===e)return;if(this.isInView=e,s&&!e&&this.hasEnteredView)return;e&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",e);const{onViewportEnter:n,onViewportLeave:i}=this.node.getProps(),o=e?n:i;o&&o(t)})}mount(){this.startObserver()}update(){if("undefined"==typeof IntersectionObserver)return;const{props:t,prevProps:e}=this.node;["amount","margin","root"].some(function({viewport:t={}},{viewport:e={}}={}){return n=>t[n]!==e[n]}(t,e))&&this.startObserver()}unmount(){}}},tap:{Feature:class extends ll{mount(){const{current:t}=this.node;t&&(this.unmount=is(t,(t,e)=>(Nl(this.node,e,"Start"),(t,{success:e})=>Nl(this.node,t,e?"End":"Cancel")),{useGlobalTarget:this.node.props.globalTapTarget}))}unmount(){}}},focus:{Feature:class extends ll{constructor(){super(...arguments),this.isActive=!1}onFocus(){let t=!1;try{t=this.node.current.matches(":focus-visible")}catch(e){t=!0}t&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){this.isActive&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=k(_r(this.node.current,"focus",()=>this.onFocus()),_r(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}},hover:{Feature:class extends ll{mount(){const{current:t}=this.node;t&&(this.unmount=_i(t,(t,e)=>(Ul(this.node,e,"Start"),t=>Ul(this.node,t,"End"))))}unmount(){}}}},_l={layout:{ProjectionNode:Jr,MeasureLayout:Fl}},ql=e.createContext({});function Zl(t){const{initial:n,animate:i}=function(t,e){if(ma(t)){const{initial:e,animate:n}=t;return{initial:!1===e||ha(e)?e:void 0,animate:ha(n)?n:void 0}}return!1!==t.inherit?e:{}}(t,e.useContext(ql));return e.useMemo(()=>({initial:n,animate:i}),[Jl(n),Jl(i)])}function Jl(t){return Array.isArray(t)?t.join(" "):t}const Ql=Symbol.for("motionComponentSymbol");function tu(t,n,i){return e.useCallback(e=>{e&&t.onMount&&t.onMount(e),n&&(e?n.mount(e):n.unmount()),i&&("function"==typeof i?i(e):fl(i)&&(i.current=e))},[n])}function eu(t,n,i,s,o){const{visualElement:r}=e.useContext(ql),a=e.useContext(ka),l=e.useContext(v),u=e.useContext(Qs).reducedMotion,c=e.useRef(null);s=s||a.renderer,!c.current&&s&&(c.current=s(t,{visualState:n,parent:r,props:i,presenceContext:l,blockInitialAnimation:!!l&&!1===l.initial,reducedMotionConfig:u}));const h=c.current,d=e.useContext(Ol);!h||h.projection||!o||"html"!==h.type&&"svg"!==h.type||function(t,e,n,i){const{layoutId:s,layout:o,drag:r,dragConstraints:a,layoutScroll:l,layoutRoot:u,layoutCrossfade:c}=e;t.projection=new n(t.latestValues,e["data-framer-portal-id"]?void 0:nu(t.parent)),t.projection.setOptions({layoutId:s,layout:o,alwaysMeasureLayout:Boolean(r)||a&&fl(a),visualElement:t,animationType:"string"==typeof o?o:"both",initialPromotionConfig:i,crossfade:c,layoutScroll:l,layoutRoot:u})}(c.current,i,o,d);const p=e.useRef(!1);e.useInsertionEffect(()=>{h&&p.current&&h.update(i,l)});const m=i[Ao],f=e.useRef(Boolean(m)&&!window.MotionHandoffIsComplete?.(m)&&window.MotionHasOptimisedAnimation?.(m));return y(()=>{h&&(p.current=!0,window.MotionIsMounted=!0,h.updateFeatures(),$i.render(h.render),f.current&&h.animationState&&h.animationState.animateChanges())}),e.useEffect(()=>{h&&(!f.current&&h.animationState&&h.animationState.animateChanges(),f.current&&(queueMicrotask(()=>{window.MotionHandoffMarkAsComplete?.(m)}),f.current=!1))}),h}function nu(t){if(t)return!1!==t.options.allowProjection?t.projection:nu(t.parent)}function iu({preloadedFeatures:t,createVisualElement:n,useRender:i,useVisualState:s,Component:o}){function r(t,r){let a;const l={...e.useContext(Qs),...t,layoutId:su(t)},{isStatic:u}=l,c=Zl(t),h=s(t,u);if(!u&&g){e.useContext(ka).strict;const t=function(t){const{drag:e,layout:n}=oa;if(!e&&!n)return{};const i={...e,...n};return{MeasureLayout:e?.isEnabled(t)||n?.isEnabled(t)?i.MeasureLayout:void 0,ProjectionNode:i.ProjectionNode}}(l);a=t.MeasureLayout,c.visualElement=eu(o,h,l,n,t.ProjectionNode)}return p(ql.Provider,{value:c,children:[a&&c.visualElement?d(a,{visualElement:c.visualElement,...l}):null,i(o,t,tu(h,c.visualElement,r),h,u,c.visualElement)]})}t&&La(t),r.displayName=`motion.${"string"==typeof o?o:`create(${o.displayName??o.name??""})`}`;const a=e.forwardRef(r);return a[Ql]=o,a}function su({layoutId:t}){const n=e.useContext(m).id;return n&&void 0!==t?n+"-"+t:t}const ou=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function ru(t,e,n){for(const i in e)Cs(e[i])||Aa(i,n)||(t[i]=e[i])}function au(t,n){const i={};return ru(i,t.style||{},t),Object.assign(i,function({transformTemplate:t},n){return e.useMemo(()=>{const e={style:{},transform:{},transformOrigin:{},vars:{}};return ba(e,n,t),Object.assign({},e.vars,e.style)},[n])}(t,n)),i}function lu(t,e){const n={},i=au(t,e);return t.drag&&!1!==t.dragListener&&(n.draggable=!1,i.userSelect=i.WebkitUserSelect=i.WebkitTouchCallout="none",i.touchAction=!0===t.drag?"none":"pan-"+("x"===t.drag?"y":"x")),void 0===t.tabIndex&&(t.onTap||t.onTapStart||t.whileTap)&&(n.tabIndex=0),n.style=i,n}const uu={offset:"stroke-dashoffset",array:"stroke-dasharray"},cu={offset:"strokeDashoffset",array:"strokeDasharray"};function hu(t,{attrX:e,attrY:n,attrScale:i,pathLength:s,pathSpacing:o=1,pathOffset:r=0,...a},l,u,c){if(ba(t,a,u),l)return void(t.style.viewBox&&(t.attrs.viewBox=t.style.viewBox));t.attrs=t.style,t.style={};const{attrs:h,style:d}=t;h.transform&&(d.transform=h.transform,delete h.transform),(d.transform||h.transformOrigin)&&(d.transformOrigin=h.transformOrigin??"50% 50%",delete h.transformOrigin),d.transform&&(d.transformBox=c?.transformBox??"fill-box",delete h.transformBox),void 0!==e&&(h.x=e),void 0!==n&&(h.y=n),void 0!==i&&(h.scale=i),void 0!==s&&function(t,e,n=1,i=0,s=!0){t.pathLength=1;const o=s?uu:cu;t[o.offset]=jt.transform(-i);const r=jt.transform(e),a=jt.transform(n);t[o.array]=`${r} ${a}`}(h,s,o,r,!1)}const du=()=>({style:{},transform:{},transformOrigin:{},vars:{},attrs:{}}),pu=t=>"string"==typeof t&&"svg"===t.toLowerCase();function mu(t,n,i,s){const o=e.useMemo(()=>{const e={style:{},transform:{},transformOrigin:{},vars:{},attrs:{}};return hu(e,n,pu(s),t.transformTemplate,t.style),{...e.attrs,style:{...e.style}}},[n]);if(t.style){const e={};ru(e,t.style,t),o.style={...e,...o.style}}return o}const fu=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function gu(t){return"string"==typeof t&&!t.includes("-")&&!!(fu.indexOf(t)>-1||/[A-Z]/u.test(t))}function yu(t=!1){return(n,i,s,{latestValues:o},r)=>{const a=(gu(n)?mu:lu)(i,o,r,n),l=Wa(i,"string"==typeof n,t),u=n!==e.Fragment?{...l,...a,ref:s}:{},{children:c}=i,h=e.useMemo(()=>Cs(c)?c.get():c,[c]);return e.createElement(n,{...u,children:h})}}const vu=t=>(n,i)=>{const s=e.useContext(ql),o=e.useContext(v),r=()=>function({scrapeMotionValuesFromProps:t,createRenderState:e},n,i,s){return{latestValues:xu(n,i,s,t),renderState:e()}}(t,n,s,o);return i?r():f(r)};function xu(t,e,n,i){const s={},o=i(t,{});for(const t in o)s[t]=Do(o[t]);let{initial:r,animate:a}=t;const l=ma(t),u=fa(t);e&&u&&!l&&!1!==t.inherit&&(void 0===r&&(r=e.initial),void 0===a&&(a=e.animate));let c=!!n&&!1===n.initial;c=c||!1===r;const h=c?a:r;if(h&&"boolean"!=typeof h&&!ca(h)){const e=Array.isArray(h)?h:[h];for(let n=0;ngu(t)?new Eu(n):new Va(n,{allowProjection:t!==e.Fragment}),Mu=Na(Su({...cl,...Gl,...Wl,..._l},Au));function Vu({children:t,as:n="ul",axis:i="y",onReorder:s,values:o,...r},a){const l=f(()=>Mu[n]),u=[],c=e.useRef(!1),h={axis:i,registerItem:(t,e)=>{const n=u.findIndex(e=>t===e.value);-1!==n?u[n].layout=e[i]:u.push({value:t,layout:e[i]}),u.sort(Du)},updateOrder:(t,e,n)=>{if(c.current)return;const i=function(t,e,n,i){if(!i)return t;const s=t.findIndex(t=>t.value===e);if(-1===s)return t;const o=i>0?1:-1,r=t[s+o];if(!r)return t;const a=t[s],l=r.layout,u=Qt(l.min,l.max,.5);return 1===o&&a.layout.max+n>u||-1===o&&a.layout.min+n-1!==o.indexOf(t))))}};return e.useEffect(()=>{c.current=!1}),d(l,{...r,ref:a,ignoreStrict:!0,children:d(Ua.Provider,{value:h,children:t})})}const Cu=e.forwardRef(Vu);function Ru(t){return t.value}function Du(t,e){return t.layout.min-e.layout.min}function ku(t){const n=f(()=>ji(t)),{isStatic:i}=e.useContext(Qs);if(i){const[,i]=e.useState(t);e.useEffect(()=>n.on("change",i),[])}return n}function Lu(t,e){const n=ku(e()),i=()=>n.set(e());return i(),y(()=>{const e=()=>lt.preRender(i,!1,!0),n=t.map(t=>t.on("change",e));return()=>{n.forEach(t=>t()),ut(i)}}),n}function Ou(t,e,n,i){if("function"==typeof t)return function(t){Li.current=[],t();const e=Lu(Li.current,t);return Li.current=void 0,e}(t);const s="function"==typeof e?e:Ms(e,n,i);return Array.isArray(t)?ju(t,s):ju([t],([t])=>s(t))}function ju(t,e){const n=f(()=>[]);return Lu(t,()=>{n.length=0;const i=t.length;for(let e=0;eMu[s]),c=e.useContext(Ua),h={x:Bu(n.x),y:Bu(n.y)},p=Ou([h.x,h.y],([t,e])=>t||e?1:"unset"),{axis:m,registerItem:g,updateOrder:y}=c;return d(u,{drag:m,...a,dragSnapToOrigin:!0,style:{...n,x:h.x,y:h.y,zIndex:p},layout:r,onDrag:(t,e)=>{const{velocity:n}=e;n[m]&&y(i,h[m].get(),n[m]),o&&o(t,e)},onLayoutMeasure:t=>g(i,t),ref:l,ignoreStrict:!0,children:t})}const Iu=e.forwardRef(Fu);var Wu=Object.freeze({__proto__:null,Group:Cu,Item:Iu});function Uu(t){return"object"==typeof t&&!Array.isArray(t)}function Nu(t,e,n,i){return"string"==typeof t&&Uu(e)?Ei(t,n,i):t instanceof NodeList?Array.from(t):Array.isArray(t)?t:[t]}function $u(t,e,n){return t*(e+1)}function zu(t,e,n,i){return"number"==typeof e?e:e.startsWith("-")||e.startsWith("+")?Math.max(0,t+parseFloat(e)):"<"===e?n:e.startsWith("<")?Math.max(0,n+parseFloat(e.slice(1))):i.get(e)??t}function Xu(t,e,n,i,s,o){!function(t,e,n){for(let i=0;ie&&s.at"number"==typeof t,Ju=t=>t.every(Zu);class Qu extends xa{constructor(){super(...arguments),this.type="object"}readValueFromInstance(t,e){if(function(t,e){return t in e}(e,t)){const n=t[e];if("string"==typeof n||"number"==typeof n)return n}}getBaseTargetFromProps(){}removeValueFromRenderState(t,e){delete e.output[t]}measureInstanceViewportBox(){return{x:{min:0,max:0},y:{min:0,max:0}}}build(t,e){Object.assign(t.output,e)}renderInstance(t,{output:e}){Object.assign(t,e)}sortInstanceNodePosition(){return 0}}function tc(t){const e={presenceContext:null,props:{},visualState:{renderState:{transform:{},transformOrigin:{},style:{},vars:{},attrs:{}},latestValues:{}}},n=os(t)&&!Es(t)?new Eu(e):new Va(e);n.mount(t),ua.set(t,n)}function ec(t){const e=new Qu({presenceContext:null,props:{},visualState:{renderState:{output:{}},latestValues:{}}});e.mount(t),ua.set(t,e)}function nc(t,e,n,i){const s=[];if(function(t,e){return Cs(t)||"number"==typeof t||"string"==typeof t&&!Uu(e)}(t,e))s.push(So(t,Uu(e)&&e.default||e,n&&n.default||n));else{const o=Nu(t,e,i),r=o.length;for(let t=0;t{const l=_u(t),{delay:u=0,times:c=$e(l),type:p="keyframes",repeat:m,repeatType:f,repeatDelay:y=0,...v}=n;let{ease:x=e.ease||"easeOut",duration:w}=n;const T="function"==typeof u?u(r,a):u,P=l.length,S=Bn(p)?p:s?.[p||"keyframes"];if(P<=2&&S){let t=100;if(2===P&&Ju(l)){const e=l[1]-l[0];t=Math.abs(e)}const e={...v};void 0!==w&&(e.duration=j(w));const n=ge(e,t,S);x=n.ease,w=n.duration}w??(w=o);const b=h+T;1===c.length&&0===c[0]&&(c[1]=1);const E=c.length-l.length;if(E>0&&Ne(c,E),1===l.length&&l.unshift(null),m){w=$u(w,m);const t=[...l],e=[...c];x=Array.isArray(x)?[...x]:[x];const n=[...x];for(let i=0;i{for(const s in t){const o=t[s];o.sort(Hu);const a=[],l=[],u=[];for(let t=0;t{i.push(...nc(n,t,e))}),i}function sc(t){return function(e,n,i){let s=[];var o;o=e,s=Array.isArray(o)&&o.some(Array.isArray)?ic(e,n,t):nc(e,n,i,t);const r=new _n(s);return t&&(t.animations.push(r),r.finished.then(()=>{w(t.animations,r)})),r}}const oc=sc();const rc=t=>function(e,n,i){return new _n(function(t,e,n,i){const s=Ei(t,i),o=s.length,r=[];for(let t=0;te.delete(n)),a.push(s)}return a}(e,n,i,t))},ac=rc(),lc={x:{length:"Width",position:"Left"},y:{length:"Height",position:"Top"}};function uc(t,e,n,i){const s=n[e],{length:o,position:r}=lc[e],a=s.current,l=n.time;s.current=t[`scroll${r}`],s.scrollLength=t[`scroll${o}`]-t[`client${o}`],s.offset.length=0,s.offset[0]=0,s.offset[1]=s.scrollLength,s.progress=L(0,s.scrollLength,s.current);const u=i-l;s.velocity=u>50?0:F(s.current-a,u)}const cc={start:0,center:.5,end:1};function hc(t,e,n=0){let i=0;if(t in cc&&(t=cc[t]),"string"==typeof t){const e=parseFloat(t);t.endsWith("px")?i=e:t.endsWith("%")?t=e/100:t.endsWith("vw")?i=e/100*document.documentElement.clientWidth:t.endsWith("vh")?i=e/100*document.documentElement.clientHeight:t=e}return"number"==typeof t&&(i=e*t),n+i}const dc=[0,0];function pc(t,e,n,i){let s=Array.isArray(t)?t:dc,o=0,r=0;return"number"==typeof t?s=[t,t]:"string"==typeof t&&(s=(t=t.trim()).includes(" ")?t.split(" "):[t,cc[t]?t:"0"]),o=hc(s[0],n,i),r=hc(s[1],e),o-r}const mc={Enter:[[0,1],[1,1]],Exit:[[0,0],[1,0]],Any:[[1,0],[0,1]],All:[[0,0],[1,1]]},fc={x:0,y:0};function gc(t,e,n){const{offset:i=mc.All}=n,{target:s=t,axis:o="y"}=n,r="y"===o?"height":"width",a=s!==t?function(t,e){const n={x:0,y:0};let i=t;for(;i&&i!==e;)if(zn(i))n.x+=i.offsetLeft,n.y+=i.offsetTop,i=i.offsetParent;else if("svg"===i.tagName){const t=i.getBoundingClientRect();i=i.parentElement;const e=i.getBoundingClientRect();n.x+=t.left-e.left,n.y+=t.top-e.top}else{if(!(i instanceof SVGGraphicsElement))break;{const{x:t,y:e}=i.getBBox();n.x+=t,n.y+=e;let s=null,o=i.parentNode;for(;!s;)"svg"===o.tagName&&(s=o),o=i.parentNode;i=s}}return n}(s,t):fc,l=s===t?{width:t.scrollWidth,height:t.scrollHeight}:function(t){return"getBBox"in t&&"svg"!==t.tagName?t.getBBox():{width:t.clientWidth,height:t.clientHeight}}(s),u={width:t.clientWidth,height:t.clientHeight};e[o].offset.length=0;let c=!e[o].interpolate;const h=i.length;for(let t=0;t{!function(t,e=t,n){if(n.x.targetOffset=0,n.y.targetOffset=0,e!==t){let i=e;for(;i&&i!==t;)n.x.targetOffset+=i.offsetLeft,n.y.targetOffset+=i.offsetTop,i=i.offsetParent}n.x.targetLength=e===t?e.scrollWidth:e.clientWidth,n.y.targetLength=e===t?e.scrollHeight:e.clientHeight,n.x.containerLength=t.clientWidth,n.y.containerLength=t.clientHeight}(t,i.target,n),function(t,e,n){uc(t,"x",e,n),uc(t,"y",e,n),e.time=n}(t,n,e),(i.offset||i.target)&&gc(t,n,i)},notify:()=>e(n)}}const vc=new WeakMap,xc=new WeakMap,wc=new WeakMap,Tc=t=>t===document.scrollingElement?window:t;function Pc(t,{container:e=document.scrollingElement,...n}={}){if(!e)return R;let i=wc.get(e);i||(i=new Set,wc.set(e,i));const s=yc(e,t,{time:0,x:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0},y:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0}},n);if(i.add(s),!vc.has(e)){const t=()=>{for(const t of i)t.measure(ct.timestamp);lt.preUpdate(n)},n=()=>{for(const t of i)t.notify()},s=()=>lt.read(t);vc.set(e,s);const o=Tc(e);window.addEventListener("resize",s,{passive:!0}),e!==document.documentElement&&xc.set(e,ys(e,s)),o.addEventListener("scroll",s,{passive:!0}),s()}const o=vc.get(e);return lt.read(o,!1,!0),()=>{ut(o);const t=wc.get(e);if(!t)return;if(t.delete(s),t.size)return;const n=vc.get(e);vc.delete(e),n&&(Tc(e).removeEventListener("scroll",n),xc.get(e)?.(),window.removeEventListener("resize",n))}}const Sc=new Map;function bc({source:t,container:e,...n}){const{axis:i}=n;t&&(e=t);const s=Sc.get(e)??new Map;Sc.set(e,s);const o=n.target??"self",r=s.get(o)??{},a=i+(n.offset??[]).join(",");return r[a]||(r[a]=!n.target&&Vn()?new ScrollTimeline({source:e,axis:i}):function(t){const e={value:0},n=Pc(n=>{e.value=100*n[t.axis].progress},t);return{currentTime:e,cancel:n}}({container:e,...n})),r[a]}function Ec(t,{axis:e="y",container:n=document.scrollingElement,...i}={}){if(!n)return R;const s={axis:e,container:n,...i};return"function"==typeof t?function(t,e){return function(t){return 2===t.length}(t)?Pc(n=>{t(n[e.axis].progress,n)},e):vs(t,bc(e))}(t,s):function(t,e){const n=bc(e);return t.attachTimeline({timeline:e.target?void 0:n,observe:t=>(t.pause(),vs(e=>{t.time=t.duration*e},n))})}(t,s)}const Ac={some:0,all:1};function Mc(t,e,{root:n,margin:i,amount:s="some"}={}){const o=Ei(t),r=new WeakMap,a=new IntersectionObserver(t=>{t.forEach(t=>{const n=r.get(t.target);if(t.isIntersecting!==Boolean(n))if(t.isIntersecting){const n=e(t.target,t);"function"==typeof n?r.set(t.target,n):a.unobserve(t.target)}else"function"==typeof n&&(n(t),r.delete(t.target))})},{root:n,rootMargin:i,threshold:"number"==typeof s?s:Ac[s]});return o.forEach(t=>a.observe(t)),()=>a.disconnect()}const Vc=Na(Su());function Cc(t){return e.useEffect(()=>()=>t(),[])}const Rc={renderer:Au,...cl,...Gl},Dc={...Rc,...Wl,..._l},kc={renderer:Au,...cl};function Lc(t,n,i){e.useInsertionEffect(()=>t.on(n,i),[t,n,i])}function Oc(t,e){Boolean(!e||e.current)}const jc=()=>({scrollX:ji(0),scrollY:ji(0),scrollXProgress:ji(0),scrollYProgress:ji(0)});function Bc({container:t,target:n,layoutEffect:i=!0,...s}={}){const o=f(jc);return(i?y:e.useEffect)(()=>(Oc(0,n),Oc(0,t),Ec((t,{x:e,y:n})=>{o.scrollX.set(e.current),o.scrollXProgress.set(e.progress),o.scrollY.set(n.current),o.scrollYProgress.set(n.progress)},{...s,container:t?.current||void 0,target:n?.current||void 0})),[t,n,JSON.stringify(s.offset)]),o}function Fc(t){const n=e.useRef(0),{isStatic:i}=e.useContext(Qs);e.useEffect(()=>{if(i)return;const e=({timestamp:e,delta:i})=>{n.current||(n.current=e),t(e-n.current,i)};return lt.update(e,!0),()=>ut(e)},[t])}class Ic extends Oi{constructor(){super(...arguments),this.isEnabled=!1}add(t){(pn.has(t)||bi.has(t))&&(this.isEnabled=!0,this.update())}update(){this.set(this.isEnabled?"transform":"auto")}}function Wc(){!aa.current&&la();const[t]=e.useState(ra.current);return t}function Uc(t,e){[...e].reverse().forEach(n=>{const i=t.getVariant(n);i&&Ha(t,i),t.variantChildren&&t.variantChildren.forEach(t=>{Uc(t,e)})})}function Nc(){const t=new Set,e={subscribe:e=>(t.add(e),()=>{t.delete(e)}),start(e,n){const i=[];return t.forEach(t=>{i.push(Ja(t,e,{transitionOverride:n}))}),Promise.all(i)},set:e=>t.forEach(t=>{!function(t,e){Array.isArray(e)?Uc(t,e):"string"==typeof e?Uc(t,[e]):Ha(t,e)}(t,e)}),stop(){t.forEach(t=>{!function(t){t.values.forEach(t=>t.stop())}(t)})},mount:()=>()=>{e.stop()}};return e}function $c(){const t=f(Nc);return y(t.mount,[]),t}const zc=$c;class Xc{constructor(){this.componentControls=new Set}subscribe(t){return this.componentControls.add(t),()=>this.componentControls.delete(t)}start(t,e){this.componentControls.forEach(n=>{n.start(t.nativeEvent||t,e)})}cancel(){this.componentControls.forEach(t=>{t.cancel()})}stop(){this.componentControls.forEach(t=>{t.stop()})}}const Yc=()=>new Xc;function Hc(t){return null!==t&&"object"==typeof t&&Ql in t}function Kc(){return Gc}function Gc(t){Zr.current&&(Zr.current.isUpdating=!1,Zr.current.blockUpdate(),t&&t())}const _c=new Map,qc=new Map,Zc=(t,e)=>`${t}: ${pn.has(e)?"transform":e}`;function Jc(t,e,n){const i=Zc(t,e),s=_c.get(i);if(!s)return null;const{animation:o,startTime:r}=s;function a(){window.MotionCancelOptimisedAnimation?.(t,e,n)}return o.onfinish=a,null===r||window.MotionHandoffIsComplete?.(t)?(a(),null):r}let Qc,th;const eh=new Set;function nh(){eh.forEach(t=>{t.animation.play(),t.animation.startTime=t.startTime}),eh.clear()}const ih=()=>({});class sh extends xa{constructor(){super(...arguments),this.measureInstanceViewportBox=lr}build(){}resetTransform(){}restoreTransform(){}removeValueFromRenderState(){}renderInstance(){}scrapeMotionValuesFromProps(){return{}}getBaseTargetFromProps(){}readValueFromInstance(t,e,n){return n.initialState[e]||0}sortInstanceNodePosition(){return 0}}const oh=vu({scrapeMotionValuesFromProps:ih,createRenderState:ih});let rh=0;const ah=t=>t>.001?1/t:1e5;t.AnimatePresence=({children:t,custom:n,initial:i=!0,onExitComplete:s,presenceAffectsLayout:o=!0,mode:r="sync",propagate:a=!1,anchorX:l="left",root:u})=>{const[c,p]=so(a),g=e.useMemo(()=>ro(t),[t]),v=a&&!c?[]:g.map(oo),x=e.useRef(!0),w=e.useRef(g),T=f(()=>new Map),[P,S]=e.useState(g),[b,E]=e.useState(g);y(()=>{x.current=!1,w.current=g;for(let t=0;t{const e=oo(t),h=!(a&&!c)&&(g===b||v.includes(e));return d(no,{isPresent:h,initial:!(x.current&&!i)&&void 0,custom:n,presenceAffectsLayout:o,mode:r,root:u,onExitComplete:h?void 0:()=>{if(!T.has(e))return;T.set(e,!0);let t=!0;T.forEach(e=>{e||(t=!1)}),t&&(M?.(),E(w.current),a&&p?.(),s&&s())},anchorX:l,children:t},e)})})},t.AnimateSharedLayout=({children:t})=>(i.useEffect(()=>{},[]),d(Da,{id:f(()=>"asl-"+rh++),children:t})),t.AsyncMotionValueAnimation=Kn,t.DOMKeyframesResolver=wi,t.DeprecatedLayoutGroupContext=ao,t.DragControls=Xc,t.FlatTree=Co,t.GroupAnimation=Gn,t.GroupAnimationWithThen=_n,t.JSAnimation=Je,t.KeyframeResolver=En,t.LayoutGroup=Da,t.LayoutGroupContext=m,t.LazyMotion=function({children:t,features:n,strict:i=!1}){const[,s]=e.useState(!Oa(n)),o=e.useRef(void 0);if(!Oa(n)){const{renderer:t,...e}=n;o.current=t,La(e)}return e.useEffect(()=>{Oa(n)&&n().then(({renderer:t,...e})=>{La(e),o.current=t,s(!0)})},[]),d(ka.Provider,{value:{renderer:o.current,strict:i},children:t})},t.MotionConfig=function({children:t,isValidProp:n,...i}){n&&Ia(n),(i={...e.useContext(Qs),...i}).isStatic=f(()=>i.isStatic);const s=e.useMemo(()=>i,[JSON.stringify(i.transition),i.transformPagePoint,i.reducedMotion]);return d(Qs.Provider,{value:s,children:t})},t.MotionConfigContext=Qs,t.MotionContext=ql,t.MotionGlobalConfig=E,t.MotionValue=Oi,t.NativeAnimation=In,t.NativeAnimationExtended=Nn,t.NativeAnimationWrapper=qn,t.PresenceContext=v,t.Reorder=Wu,t.SubscriptionManager=O,t.SwitchLayoutGroupContext=Ol,t.ViewTransitionBuilder=qs,t.VisualElement=xa,t.WillChangeMotionValue=Ic,t.acceleratedValues=bi,t.activeAnimations=ft,t.addAttrValue=Ri,t.addPointerEvent=pl,t.addPointerInfo=dl,t.addScaleCorrector=vr,t.addStyleValue=Ii,t.addUniqueItem=x,t.alpha=Pt,t.analyseComplexValue=Yt,t.animate=oc,t.animateMini=ac,t.animateValue=function(t){return new Je(t)},t.animateView=function(t,e={}){return new qs(t,e)},t.animateVisualElement=Ja,t.animationControls=Nc,t.animationMapKey=Jn,t.animations=cl,t.anticipate=K,t.applyPxDefaults=Pi,t.attachSpring=Rs,t.attrEffect=Di,t.backIn=Y,t.backInOut=H,t.backOut=X,t.buildTransform=Sa,t.calcGeneratorDuration=fe,t.calcLength=lo,t.cancelFrame=ut,t.cancelMicrotask=zi,t.cancelSync=Js,t.circIn=G,t.circInOut=q,t.circOut=_,t.clamp=P,t.collectMotionValues=Li,t.color=Ut,t.complex=_t,t.convertOffsetToTimes=ze,t.createBox=lr,t.createGeneratorEasing=ge,t.createRenderBatcher=at,t.createRendererMotionComponent=iu,t.createScopedAnimate=sc,t.cubicBezier=N,t.cubicBezierAsString=kn,t.defaultEasing=Xe,t.defaultOffset=$e,t.defaultTransformValue=ln,t.defaultValueTypes=gi,t.degrees=Lt,t.delay=Ro,t.dimensionValueTypes=ri,t.disableInstantTransitions=function(){E.instantAnimations=!1},t.distance=gl,t.distance2D=yl,t.domAnimation=Rc,t.domMax=Dc,t.domMin=kc,t.easeIn=Z,t.easeInOut=Q,t.easeOut=J,t.easingDefinitionToFunction=st,t.fillOffset=Ne,t.fillWildcards=Qe,t.filterProps=Wa,t.findDimensionValueType=ai,t.findValueType=Os,t.flushKeyframeResolvers=bn,t.frame=lt,t.frameData=ct,t.frameSteps=ht,t.generateLinearEasing=pe,t.getAnimatableNone=vi,t.getAnimationMap=Qn,t.getComputedStyle=ss,t.getDefaultValueType=yi,t.getEasingForSegment=et,t.getMixer=ae,t.getOriginIndex=As,t.getValueAsType=Mi,t.getValueTransition=ii,t.getVariableValue=ni,t.hasWarned=function(t){return I.has(t)},t.hex=Dt,t.hover=_i,t.hsla=Wt,t.hslaToRgba=Zt,t.inView=Mc,t.inertia=We,t.interpolate=Ue,t.invariant=b,t.invisibleValues=se,t.isBezierDefinition=nt,t.isBrowser=g,t.isCSSVariableName=yt,t.isCSSVariableToken=xt,t.isDragActive=Yi,t.isDragging=Xi,t.isEasingArray=tt,t.isGenerator=Bn,t.isHTMLElement=zn,t.isMotionComponent=Hc,t.isMotionValue=Cs,t.isNodeOrChild=qi,t.isNumericalString=A,t.isObject=M,t.isPrimaryPointer=Zi,t.isSVGElement=os,t.isSVGSVGElement=Es,t.isValidMotionProp=Ba,t.isWaapiSupportedEasing=function t(e){return Boolean("function"==typeof e&&Dn()||!e||"string"==typeof e&&(e in Ln||Dn())||nt(e)||Array.isArray(e)&&e.every(t))},t.isZeroValueString=V,t.keyframes=Ye,t.m=Vc,t.makeUseVisualState=vu,t.mapEasingToNativeEasing=On,t.mapValue=function(t,e,n,i){const s=Ms(e,n,i);return Vs(()=>s(t.get()))},t.maxGeneratorDuration=me,t.memo=C,t.microtask=$i,t.millisecondsToSeconds=B,t.mirrorEasing=$,t.mix=he,t.mixArray=le,t.mixColor=ie,t.mixComplex=ce,t.mixImmediate=Jt,t.mixLinearColor=te,t.mixNumber=Qt,t.mixObject=ue,t.mixVisibility=oe,t.motion=Mu,t.motionValue=ji,t.moveItem=T,t.noop=R,t.number=Tt,t.numberValueTypes=fi,t.observeTimeline=vs,t.optimizedAppearDataAttribute=Ao,t.parseCSSVariable=ei,t.parseValueFromTransform=un,t.percent=Ot,t.pipe=k,t.positionalKeys=si,t.press=is,t.progress=L,t.progressPercentage=It,t.propEffect=ki,t.px=jt,t.readTransformValue=cn,t.recordStats=function(){if(rt.value)throw Ss(),new Error("Stats are already being measured");const t=rt;return t.value={frameloop:{setup:[],rate:[],read:[],resolveKeyframes:[],preUpdate:[],update:[],preRender:[],render:[],postRender:[]},animations:{mainThread:[],waapi:[],layout:[]},layoutProjection:{nodes:[],calculatedTargetDeltas:[],calculatedProjections:[]}},t.addProjectionMetrics=e=>{const{layoutProjection:n}=t.value;n.nodes.push(e.nodes),n.calculatedTargetDeltas.push(e.calculatedTargetDeltas),n.calculatedProjections.push(e.calculatedProjections)},lt.postRender(xs,!0),bs},t.removeItem=w,t.resize=ys,t.resolveElements=Ei,t.resolveMotionValue=Do,t.reverseEasing=z,t.rgbUnit=Ct,t.rgba=Rt,t.scale=St,t.scroll=Ec,t.scrollInfo=Pc,t.secondsToMilliseconds=j,t.setDragLock=Hi,t.setStyle=Mn,t.spring=Ie,t.springValue=function(t,e){const n=ji(Cs(t)?t.get():t);return Rs(n,t,e),n},t.stagger=function(t=.1,{startDelay:e=0,from:n=0,ease:i}={}){return(s,o)=>{const r="number"==typeof n?n:As(n,o),a=Math.abs(r-s);let l=t*a;if(i){const e=o*t;l=st(i)(l/e)*e}return e+l}},t.startOptimizedAppearAnimation=function(t,e,n,i,s){if(window.MotionIsMounted)return;const o=t.dataset[Eo];if(!o)return;window.MotionHandoffAnimation=Jc;const r=Zc(o,e);th||(th=jn(t,e,[n[0],n[0]],{duration:1e4,ease:"linear"}),_c.set(r,{animation:th,startTime:null}),window.MotionHandoffAnimation=Jc,window.MotionHasOptimisedAnimation=(t,e)=>{if(!t)return!1;if(!e)return qc.has(t);const n=Zc(t,e);return Boolean(_c.get(n))},window.MotionHandoffMarkAsComplete=t=>{qc.has(t)&&qc.set(t,!0)},window.MotionHandoffIsComplete=t=>!0===qc.get(t),window.MotionCancelOptimisedAnimation=(t,e,n,i)=>{const s=Zc(t,e),o=_c.get(s);o&&(n&&void 0===i?n.postRender(()=>{n.postRender(()=>{o.animation.cancel()})}):o.animation.cancel(),n&&i?(eh.add(o),n.render(nh)):(_c.delete(s),_c.size||(window.MotionCancelOptimisedAnimation=void 0)))},window.MotionCheckAppearSync=(t,e,n)=>{const i=Mo(t);if(!i)return;const s=window.MotionHasOptimisedAnimation?.(i,e),o=t.props.values?.[e];if(!s||!o)return;const r=n.on("change",t=>{o.get()!==t&&(window.MotionCancelOptimisedAnimation?.(i,e),r())});return r});const a=()=>{th.cancel();const o=jn(t,e,n,i);void 0===Qc&&(Qc=performance.now()),o.startTime=Qc,_c.set(r,{animation:o,startTime:Qc}),s&&s(o)};qc.set(o,!1),th.ready?th.ready.then(a).catch(R):a()},t.startWaapiAnimation=jn,t.statsBuffer=rt,t.steps=function(t,e="end"){return n=>{const i=(n="end"===e?Math.min(n,.999):Math.max(n,.001))*t,s="end"===e?Math.floor(i):Math.ceil(i);return P(0,1,s/t)}},t.styleEffect=Wi,t.supportedWaapiEasing=Ln,t.supportsBrowserAnimation=Hn,t.supportsFlags=Cn,t.supportsLinearEasing=Dn,t.supportsPartialKeyframes=Si,t.supportsScrollTimeline=Vn,t.svgEffect=Ni,t.sync=Zs,t.testValueType=oi,t.time=mt,t.transform=Ms,t.transformPropOrder=dn,t.transformProps=pn,t.transformValue=Vs,t.transformValueTypes=mi,t.unwrapMotionComponent=function(t){if(Hc(t))return t[Ql]},t.useAnimate=function(){const t=f(()=>({current:null,animations:[]})),e=f(()=>sc(t));return Cc(()=>{t.animations.forEach(t=>t.stop()),t.animations.length=0}),[t,e]},t.useAnimateMini=function(){const t=f(()=>({current:null,animations:[]})),e=f(()=>rc(t));return Cc(()=>{t.animations.forEach(t=>t.stop())}),[t,e]},t.useAnimation=zc,t.useAnimationControls=$c,t.useAnimationFrame=Fc,t.useCycle=function(...t){const n=e.useRef(0),[i,s]=e.useState(t[n.current]),o=e.useCallback(e=>{n.current="number"!=typeof e?W(0,t.length,n.current+1):e,s(t[n.current])},[t.length,...t]);return[i,o]},t.useDeprecatedAnimatedState=function(t){const[n,i]=e.useState(t),s=oh({},!1),o=f(()=>new sh({props:{onUpdate:t=>{i({...t})}},visualState:s,presenceContext:null},{initialState:t}));return e.useLayoutEffect(()=>(o.mount({}),()=>o.unmount()),[o]),[n,f(()=>t=>Ja(o,t))]},t.useDeprecatedInvertedScale=function(t){let n=ku(1),i=ku(1);const{visualElement:s}=e.useContext(ql);return t?(n=t.scaleX||n,i=t.scaleY||i):s&&(n=s.getValue("scaleX",1),i=s.getValue("scaleY",1)),{scaleX:Ou(n,ah),scaleY:Ou(i,ah)}},t.useDomEvent=function(t,n,i,s){e.useEffect(()=>{const e=t.current;if(i&&e)return _r(e,n,i,s)},[t,n,i,s])},t.useDragControls=function(){return f(Yc)},t.useElementScroll=function(t){return Bc({container:t})},t.useForceUpdate=Ca,t.useInView=function(t,{root:n,margin:i,amount:s,once:o=!1,initial:r=!1}={}){const[a,l]=e.useState(r);return e.useEffect(()=>{if(!t.current||o&&a)return;const e={root:n&&n.current||void 0,margin:i,amount:s};return Mc(t.current,()=>(l(!0),o?void 0:()=>l(!1)),e)},[n,t,i,o,s]),a},t.useInstantLayoutTransition=Kc,t.useInstantTransition=function(){const[t,n]=Ca(),i=Kc(),s=e.useRef(-1);return e.useEffect(()=>{lt.postRender(()=>lt.postRender(()=>{n===s.current&&(E.instantAnimations=!1)}))},[n]),e=>{i(()=>{E.instantAnimations=!0,t(),e(),s.current=n+1})}},t.useIsPresent=function(){return null===(t=e.useContext(v))||t.isPresent;var t},t.useIsomorphicLayoutEffect=y,t.useMotionTemplate=function(t,...e){const n=t.length;return Lu(e.filter(Cs),function(){let i="";for(let s=0;s{const t=()=>n(!document.hidden);return document.hidden&&t(),document.addEventListener("visibilitychange",t),()=>{document.removeEventListener("visibilitychange",t)}},[]),t},t.usePresence=so,t.usePresenceData=function(){const t=e.useContext(v);return t?t.custom:void 0},t.useReducedMotion=Wc,t.useReducedMotionConfig=function(){const t=Wc(),{reducedMotion:n}=e.useContext(Qs);return"never"!==n&&("always"===n||t)},t.useResetProjection=function(){return e.useCallback(()=>{const t=Zr.current;t&&t.resetTree()},[])},t.useScroll=Bc,t.useSpring=function(t,n={}){const{isStatic:i}=e.useContext(Qs),s=()=>Cs(t)?t.get():t;if(i)return Ou(s);const o=ku(s());return e.useInsertionEffect(()=>Rs(o,t,n),[o,JSON.stringify(n)]),o},t.useTime=function(){const t=ku(0);return Fc(e=>t.set(e)),t},t.useTransform=Ou,t.useUnmountEffect=Cc,t.useVelocity=function(t){const e=ku(t.getVelocity()),n=()=>{const i=t.getVelocity();e.set(i),i&<.update(n)};return Lc(t,"change",()=>{lt.update(n,!1,!0)}),e},t.useViewportScroll=function(){return Bc()},t.useWillChange=function(){return f(()=>new Ic("auto"))},t.velocityPerSecond=F,t.vh=Bt,t.visualElementStore=ua,t.vw=Ft,t.warnOnce=function(t,e,n){t||I.has(e)||(console.warn(e),n&&console.warn(n),I.add(e))},t.warning=S,t.wrap=W}); diff --git a/node_modules/framer-motion/dist/m.d.ts b/node_modules/framer-motion/dist/m.d.ts new file mode 100644 index 00000000..aad6586a --- /dev/null +++ b/node_modules/framer-motion/dist/m.d.ts @@ -0,0 +1,433 @@ +/// +import { MotionNodeOptions, MotionValue, TransformProperties, SVGPathProperties, Batcher } from 'motion-dom'; +import * as React$1 from 'react'; +import { CSSProperties, PropsWithoutRef, RefAttributes, JSX, SVGAttributes } from 'react'; + +type MotionValueString = MotionValue; +type MotionValueNumber = MotionValue; +type MotionValueAny = MotionValue; +type AnyMotionValue = MotionValueNumber | MotionValueString | MotionValueAny; +type MotionValueHelper = T | AnyMotionValue; +type MakeMotionHelper = { + [K in keyof T]: MotionValueHelper; +}; +type MakeCustomValueTypeHelper = MakeMotionHelper; +type MakeMotion = MakeCustomValueTypeHelper; +type MotionCSS = MakeMotion>; +/** + * @public + */ +type MotionTransform = MakeMotion; +type MotionSVGProps = MakeMotion; +/** + * @public + */ +interface MotionStyle$1 extends MotionCSS, MotionTransform, MotionSVGProps { +} +/** + * Props for `motion` components. + * + * @public + */ +interface MotionProps extends MotionNodeOptions { + /** + * + * The React DOM `style` prop, enhanced with support for `MotionValue`s and separate `transform` values. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * + * return + * } + * ``` + */ + style?: MotionStyle$1; + children?: React.ReactNode | MotionValueNumber | MotionValueString; +} + +type MotionComponentProps = { + [K in Exclude]?: Props[K]; +} & MotionProps; + +interface HTMLElements { + a: HTMLAnchorElement; + abbr: HTMLElement; + address: HTMLElement; + area: HTMLAreaElement; + article: HTMLElement; + aside: HTMLElement; + audio: HTMLAudioElement; + b: HTMLElement; + base: HTMLBaseElement; + bdi: HTMLElement; + bdo: HTMLElement; + big: HTMLElement; + blockquote: HTMLQuoteElement; + body: HTMLBodyElement; + br: HTMLBRElement; + button: HTMLButtonElement; + canvas: HTMLCanvasElement; + caption: HTMLElement; + center: HTMLElement; + cite: HTMLElement; + code: HTMLElement; + col: HTMLTableColElement; + colgroup: HTMLTableColElement; + data: HTMLDataElement; + datalist: HTMLDataListElement; + dd: HTMLElement; + del: HTMLModElement; + details: HTMLDetailsElement; + dfn: HTMLElement; + dialog: HTMLDialogElement; + div: HTMLDivElement; + dl: HTMLDListElement; + dt: HTMLElement; + em: HTMLElement; + embed: HTMLEmbedElement; + fieldset: HTMLFieldSetElement; + figcaption: HTMLElement; + figure: HTMLElement; + footer: HTMLElement; + form: HTMLFormElement; + h1: HTMLHeadingElement; + h2: HTMLHeadingElement; + h3: HTMLHeadingElement; + h4: HTMLHeadingElement; + h5: HTMLHeadingElement; + h6: HTMLHeadingElement; + head: HTMLHeadElement; + header: HTMLElement; + hgroup: HTMLElement; + hr: HTMLHRElement; + html: HTMLHtmlElement; + i: HTMLElement; + iframe: HTMLIFrameElement; + img: HTMLImageElement; + input: HTMLInputElement; + ins: HTMLModElement; + kbd: HTMLElement; + keygen: HTMLElement; + label: HTMLLabelElement; + legend: HTMLLegendElement; + li: HTMLLIElement; + link: HTMLLinkElement; + main: HTMLElement; + map: HTMLMapElement; + mark: HTMLElement; + menu: HTMLElement; + menuitem: HTMLElement; + meta: HTMLMetaElement; + meter: HTMLMeterElement; + nav: HTMLElement; + noindex: HTMLElement; + noscript: HTMLElement; + object: HTMLObjectElement; + ol: HTMLOListElement; + optgroup: HTMLOptGroupElement; + option: HTMLOptionElement; + output: HTMLOutputElement; + p: HTMLParagraphElement; + param: HTMLParamElement; + picture: HTMLElement; + pre: HTMLPreElement; + progress: HTMLProgressElement; + q: HTMLQuoteElement; + rp: HTMLElement; + rt: HTMLElement; + ruby: HTMLElement; + s: HTMLElement; + samp: HTMLElement; + search: HTMLElement; + slot: HTMLSlotElement; + script: HTMLScriptElement; + section: HTMLElement; + select: HTMLSelectElement; + small: HTMLElement; + source: HTMLSourceElement; + span: HTMLSpanElement; + strong: HTMLElement; + style: HTMLStyleElement; + sub: HTMLElement; + summary: HTMLElement; + sup: HTMLElement; + table: HTMLTableElement; + template: HTMLTemplateElement; + tbody: HTMLTableSectionElement; + td: HTMLTableDataCellElement; + textarea: HTMLTextAreaElement; + tfoot: HTMLTableSectionElement; + th: HTMLTableHeaderCellElement; + thead: HTMLTableSectionElement; + time: HTMLTimeElement; + title: HTMLTitleElement; + tr: HTMLTableRowElement; + track: HTMLTrackElement; + u: HTMLElement; + ul: HTMLUListElement; + var: HTMLElement; + video: HTMLVideoElement; + wbr: HTMLElement; + webview: HTMLWebViewElement; +} + +/** + * @public + */ +type ForwardRefComponent = { + readonly $$typeof: symbol; +} & ((props: PropsWithoutRef

& RefAttributes) => JSX.Element); +type AttributesWithoutMotionProps = { + [K in Exclude]?: Attributes[K]; +}; +/** + * @public + */ +type HTMLMotionProps = AttributesWithoutMotionProps & MotionProps; +/** + * Motion-optimised versions of React's HTML components. + * + * @public + */ +type HTMLMotionComponents = { + [K in keyof HTMLElements]: ForwardRefComponent>; +}; + +type UnionStringArray> = T[number]; +declare const svgElements: readonly ["animate", "circle", "defs", "desc", "ellipse", "g", "image", "line", "filter", "marker", "mask", "metadata", "path", "pattern", "polygon", "polyline", "rect", "stop", "svg", "switch", "symbol", "text", "tspan", "use", "view", "clipPath", "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence", "foreignObject", "linearGradient", "radialGradient", "textPath"]; +type SVGElements = UnionStringArray; + +interface SVGAttributesWithoutMotionProps extends Pick, Exclude, keyof MotionProps>> { +} +/** + * Blanket-accept any SVG attribute as a `MotionValue` + * @public + */ +type SVGAttributesAsMotionValues = MakeMotion>; +type UnwrapSVGFactoryElement = F extends React.SVGProps ? P : never; +/** + * @public + */ +interface SVGMotionProps extends SVGAttributesAsMotionValues, MotionProps { +} +/** + * Motion-optimised versions of React's SVG components. + * + * @public + */ +type SVGMotionComponents = { + [K in SVGElements]: ForwardRefComponent, SVGMotionProps>>; +}; + +declare const optimizedAppearDataAttribute: "data-framer-appear-id"; + +/** + * Expose only the needed part of the VisualElement interface to + * ensure React types don't end up in the generic DOM bundle. + */ +interface WithAppearProps { + props: { + [optimizedAppearDataAttribute]?: string; + values?: { + [key: string]: MotionValue | MotionValue; + }; + }; +} +type HandoffFunction = (storeId: string, valueName: string, frame: Batcher) => number | null; +/** + * The window global object acts as a bridge between our inline script + * triggering the optimized appear animations, and Motion. + */ +declare global { + interface Window { + MotionHandoffAnimation?: HandoffFunction; + MotionHandoffMarkAsComplete?: (elementId: string) => void; + MotionHandoffIsComplete?: (elementId: string) => boolean; + MotionHasOptimisedAnimation?: (elementId?: string, valueName?: string) => boolean; + MotionCancelOptimisedAnimation?: (elementId?: string, valueName?: string, frame?: Batcher, canResume?: boolean) => void; + MotionCheckAppearSync?: (visualElement: WithAppearProps, valueName: string, value: MotionValue) => VoidFunction | void; + MotionIsMounted?: boolean; + } +} + +type DOMMotionComponents = HTMLMotionComponents & SVGMotionComponents; + +declare const createMinimalMotionComponent: (Component: string | TagName | React$1.ComponentType, { forwardMotionProps }?: { + forwardMotionProps: boolean; +}) => TagName extends "symbol" | "animate" | "clipPath" | "filter" | "marker" | "mask" | "path" | "text" | "circle" | "stop" | keyof HTMLElements | "svg" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "switch" | "textPath" | "tspan" | "use" | "view" ? DOMMotionComponents[TagName] : React$1.ComponentType, "children"> & { + children?: ("children" extends keyof Props ? Props[keyof Props & "children"] | MotionComponentProps[keyof Props & "children"] : MotionComponentProps["children"]) | undefined; +}>; + +/** + * HTML components + */ +declare const MotionA: ForwardRefComponent>; +declare const MotionAbbr: ForwardRefComponent>; +declare const MotionAddress: ForwardRefComponent>; +declare const MotionArea: ForwardRefComponent>; +declare const MotionArticle: ForwardRefComponent>; +declare const MotionAside: ForwardRefComponent>; +declare const MotionAudio: ForwardRefComponent>; +declare const MotionB: ForwardRefComponent>; +declare const MotionBase: ForwardRefComponent>; +declare const MotionBdi: ForwardRefComponent>; +declare const MotionBdo: ForwardRefComponent>; +declare const MotionBig: ForwardRefComponent>; +declare const MotionBlockquote: ForwardRefComponent>; +declare const MotionBody: ForwardRefComponent>; +declare const MotionButton: ForwardRefComponent>; +declare const MotionCanvas: ForwardRefComponent>; +declare const MotionCaption: ForwardRefComponent>; +declare const MotionCite: ForwardRefComponent>; +declare const MotionCode: ForwardRefComponent>; +declare const MotionCol: ForwardRefComponent>; +declare const MotionColgroup: ForwardRefComponent>; +declare const MotionData: ForwardRefComponent>; +declare const MotionDatalist: ForwardRefComponent>; +declare const MotionDd: ForwardRefComponent>; +declare const MotionDel: ForwardRefComponent>; +declare const MotionDetails: ForwardRefComponent>; +declare const MotionDfn: ForwardRefComponent>; +declare const MotionDialog: ForwardRefComponent>; +declare const MotionDiv: ForwardRefComponent>; +declare const MotionDl: ForwardRefComponent>; +declare const MotionDt: ForwardRefComponent>; +declare const MotionEm: ForwardRefComponent>; +declare const MotionEmbed: ForwardRefComponent>; +declare const MotionFieldset: ForwardRefComponent>; +declare const MotionFigcaption: ForwardRefComponent>; +declare const MotionFigure: ForwardRefComponent>; +declare const MotionFooter: ForwardRefComponent>; +declare const MotionForm: ForwardRefComponent>; +declare const MotionH1: ForwardRefComponent>; +declare const MotionH2: ForwardRefComponent>; +declare const MotionH3: ForwardRefComponent>; +declare const MotionH4: ForwardRefComponent>; +declare const MotionH5: ForwardRefComponent>; +declare const MotionH6: ForwardRefComponent>; +declare const MotionHead: ForwardRefComponent>; +declare const MotionHeader: ForwardRefComponent>; +declare const MotionHgroup: ForwardRefComponent>; +declare const MotionHr: ForwardRefComponent>; +declare const MotionHtml: ForwardRefComponent>; +declare const MotionI: ForwardRefComponent>; +declare const MotionIframe: ForwardRefComponent>; +declare const MotionImg: ForwardRefComponent>; +declare const MotionInput: ForwardRefComponent>; +declare const MotionIns: ForwardRefComponent>; +declare const MotionKbd: ForwardRefComponent>; +declare const MotionKeygen: ForwardRefComponent>; +declare const MotionLabel: ForwardRefComponent>; +declare const MotionLegend: ForwardRefComponent>; +declare const MotionLi: ForwardRefComponent>; +declare const MotionLink: ForwardRefComponent>; +declare const MotionMain: ForwardRefComponent>; +declare const MotionMap: ForwardRefComponent>; +declare const MotionMark: ForwardRefComponent>; +declare const MotionMenu: ForwardRefComponent>; +declare const MotionMenuitem: ForwardRefComponent>; +declare const MotionMeter: ForwardRefComponent>; +declare const MotionNav: ForwardRefComponent>; +declare const MotionObject: ForwardRefComponent>; +declare const MotionOl: ForwardRefComponent>; +declare const MotionOptgroup: ForwardRefComponent>; +declare const MotionOption: ForwardRefComponent>; +declare const MotionOutput: ForwardRefComponent>; +declare const MotionP: ForwardRefComponent>; +declare const MotionParam: ForwardRefComponent>; +declare const MotionPicture: ForwardRefComponent>; +declare const MotionPre: ForwardRefComponent>; +declare const MotionProgress: ForwardRefComponent>; +declare const MotionQ: ForwardRefComponent>; +declare const MotionRp: ForwardRefComponent>; +declare const MotionRt: ForwardRefComponent>; +declare const MotionRuby: ForwardRefComponent>; +declare const MotionS: ForwardRefComponent>; +declare const MotionSamp: ForwardRefComponent>; +declare const MotionScript: ForwardRefComponent>; +declare const MotionSection: ForwardRefComponent>; +declare const MotionSelect: ForwardRefComponent>; +declare const MotionSmall: ForwardRefComponent>; +declare const MotionSource: ForwardRefComponent>; +declare const MotionSpan: ForwardRefComponent>; +declare const MotionStrong: ForwardRefComponent>; +declare const MotionStyle: ForwardRefComponent>; +declare const MotionSub: ForwardRefComponent>; +declare const MotionSummary: ForwardRefComponent>; +declare const MotionSup: ForwardRefComponent>; +declare const MotionTable: ForwardRefComponent>; +declare const MotionTbody: ForwardRefComponent>; +declare const MotionTd: ForwardRefComponent>; +declare const MotionTextarea: ForwardRefComponent>; +declare const MotionTfoot: ForwardRefComponent>; +declare const MotionTh: ForwardRefComponent>; +declare const MotionThead: ForwardRefComponent>; +declare const MotionTime: ForwardRefComponent>; +declare const MotionTitle: ForwardRefComponent>; +declare const MotionTr: ForwardRefComponent>; +declare const MotionTrack: ForwardRefComponent>; +declare const MotionU: ForwardRefComponent>; +declare const MotionUl: ForwardRefComponent>; +declare const MotionVideo: ForwardRefComponent>; +declare const MotionWbr: ForwardRefComponent>; +declare const MotionWebview: ForwardRefComponent>; +/** + * SVG components + */ +declare const MotionAnimate: ForwardRefComponent>; +declare const MotionCircle: ForwardRefComponent>; +declare const MotionDefs: ForwardRefComponent>; +declare const MotionDesc: ForwardRefComponent>; +declare const MotionEllipse: ForwardRefComponent>; +declare const MotionG: ForwardRefComponent>; +declare const MotionImage: ForwardRefComponent>; +declare const MotionLine: ForwardRefComponent>; +declare const MotionFilter: ForwardRefComponent>; +declare const MotionMarker: ForwardRefComponent>; +declare const MotionMask: ForwardRefComponent>; +declare const MotionMetadata: ForwardRefComponent>; +declare const MotionPath: ForwardRefComponent>; +declare const MotionPattern: ForwardRefComponent>; +declare const MotionPolygon: ForwardRefComponent>; +declare const MotionPolyline: ForwardRefComponent>; +declare const MotionRect: ForwardRefComponent>; +declare const MotionStop: ForwardRefComponent>; +declare const MotionSvg: ForwardRefComponent>; +declare const MotionSymbol: ForwardRefComponent>; +declare const MotionText: ForwardRefComponent>; +declare const MotionTspan: ForwardRefComponent>; +declare const MotionUse: ForwardRefComponent>; +declare const MotionView: ForwardRefComponent>; +declare const MotionClipPath: ForwardRefComponent>; +declare const MotionFeBlend: ForwardRefComponent>; +declare const MotionFeColorMatrix: ForwardRefComponent>; +declare const MotionFeComponentTransfer: ForwardRefComponent>; +declare const MotionFeComposite: ForwardRefComponent>; +declare const MotionFeConvolveMatrix: ForwardRefComponent>; +declare const MotionFeDiffuseLighting: ForwardRefComponent>; +declare const MotionFeDisplacementMap: ForwardRefComponent>; +declare const MotionFeDistantLight: ForwardRefComponent>; +declare const MotionFeDropShadow: ForwardRefComponent>; +declare const MotionFeFlood: ForwardRefComponent>; +declare const MotionFeFuncA: ForwardRefComponent>; +declare const MotionFeFuncB: ForwardRefComponent>; +declare const MotionFeFuncG: ForwardRefComponent>; +declare const MotionFeFuncR: ForwardRefComponent>; +declare const MotionFeGaussianBlur: ForwardRefComponent>; +declare const MotionFeImage: ForwardRefComponent>; +declare const MotionFeMerge: ForwardRefComponent>; +declare const MotionFeMergeNode: ForwardRefComponent>; +declare const MotionFeMorphology: ForwardRefComponent>; +declare const MotionFeOffset: ForwardRefComponent>; +declare const MotionFePointLight: ForwardRefComponent>; +declare const MotionFeSpecularLighting: ForwardRefComponent>; +declare const MotionFeSpotLight: ForwardRefComponent>; +declare const MotionFeTile: ForwardRefComponent>; +declare const MotionFeTurbulence: ForwardRefComponent>; +declare const MotionForeignObject: ForwardRefComponent>; +declare const MotionLinearGradient: ForwardRefComponent>; +declare const MotionRadialGradient: ForwardRefComponent>; +declare const MotionTextPath: ForwardRefComponent>; + +export { MotionA as a, MotionAbbr as abbr, MotionAddress as address, MotionAnimate as animate, MotionArea as area, MotionArticle as article, MotionAside as aside, MotionAudio as audio, MotionB as b, MotionBase as base, MotionBdi as bdi, MotionBdo as bdo, MotionBig as big, MotionBlockquote as blockquote, MotionBody as body, MotionButton as button, MotionCanvas as canvas, MotionCaption as caption, MotionCircle as circle, MotionCite as cite, MotionClipPath as clipPath, MotionCode as code, MotionCol as col, MotionColgroup as colgroup, createMinimalMotionComponent as create, MotionData as data, MotionDatalist as datalist, MotionDd as dd, MotionDefs as defs, MotionDel as del, MotionDesc as desc, MotionDetails as details, MotionDfn as dfn, MotionDialog as dialog, MotionDiv as div, MotionDl as dl, MotionDt as dt, MotionEllipse as ellipse, MotionEm as em, MotionEmbed as embed, MotionFeBlend as feBlend, MotionFeColorMatrix as feColorMatrix, MotionFeComponentTransfer as feComponentTransfer, MotionFeComposite as feComposite, MotionFeConvolveMatrix as feConvolveMatrix, MotionFeDiffuseLighting as feDiffuseLighting, MotionFeDisplacementMap as feDisplacementMap, MotionFeDistantLight as feDistantLight, MotionFeDropShadow as feDropShadow, MotionFeFlood as feFlood, MotionFeFuncA as feFuncA, MotionFeFuncB as feFuncB, MotionFeFuncG as feFuncG, MotionFeFuncR as feFuncR, MotionFeGaussianBlur as feGaussianBlur, MotionFeImage as feImage, MotionFeMerge as feMerge, MotionFeMergeNode as feMergeNode, MotionFeMorphology as feMorphology, MotionFeOffset as feOffset, MotionFePointLight as fePointLight, MotionFeSpecularLighting as feSpecularLighting, MotionFeSpotLight as feSpotLight, MotionFeTile as feTile, MotionFeTurbulence as feTurbulence, MotionFieldset as fieldset, MotionFigcaption as figcaption, MotionFigure as figure, MotionFilter as filter, MotionFooter as footer, MotionForeignObject as foreignObject, MotionForm as form, MotionG as g, MotionH1 as h1, MotionH2 as h2, MotionH3 as h3, MotionH4 as h4, MotionH5 as h5, MotionH6 as h6, MotionHead as head, MotionHeader as header, MotionHgroup as hgroup, MotionHr as hr, MotionHtml as html, MotionI as i, MotionIframe as iframe, MotionImage as image, MotionImg as img, MotionInput as input, MotionIns as ins, MotionKbd as kbd, MotionKeygen as keygen, MotionLabel as label, MotionLegend as legend, MotionLi as li, MotionLine as line, MotionLinearGradient as linearGradient, MotionLink as link, MotionMain as main, MotionMap as map, MotionMark as mark, MotionMarker as marker, MotionMask as mask, MotionMenu as menu, MotionMenuitem as menuitem, MotionMetadata as metadata, MotionMeter as meter, MotionNav as nav, MotionObject as object, MotionOl as ol, MotionOptgroup as optgroup, MotionOption as option, MotionOutput as output, MotionP as p, MotionParam as param, MotionPath as path, MotionPattern as pattern, MotionPicture as picture, MotionPolygon as polygon, MotionPolyline as polyline, MotionPre as pre, MotionProgress as progress, MotionQ as q, MotionRadialGradient as radialGradient, MotionRect as rect, MotionRp as rp, MotionRt as rt, MotionRuby as ruby, MotionS as s, MotionSamp as samp, MotionScript as script, MotionSection as section, MotionSelect as select, MotionSmall as small, MotionSource as source, MotionSpan as span, MotionStop as stop, MotionStrong as strong, MotionStyle as style, MotionSub as sub, MotionSummary as summary, MotionSup as sup, MotionSvg as svg, MotionSymbol as symbol, MotionTable as table, MotionTbody as tbody, MotionTd as td, MotionText as text, MotionTextPath as textPath, MotionTextarea as textarea, MotionTfoot as tfoot, MotionTh as th, MotionThead as thead, MotionTime as time, MotionTitle as title, MotionTr as tr, MotionTrack as track, MotionTspan as tspan, MotionU as u, MotionUl as ul, MotionUse as use, MotionVideo as video, MotionView as view, MotionWbr as wbr, MotionWebview as webview }; diff --git a/node_modules/framer-motion/dist/mini.d.ts b/node_modules/framer-motion/dist/mini.d.ts new file mode 100644 index 00000000..0c2d756c --- /dev/null +++ b/node_modules/framer-motion/dist/mini.d.ts @@ -0,0 +1,6 @@ +import * as motion_dom from 'motion-dom'; +import { AnimationScope } from 'motion-dom'; + +declare function useAnimateMini(): [AnimationScope, (elementOrSelector: motion_dom.ElementOrSelector, keyframes: motion_dom.DOMKeyframesDefinition, options?: motion_dom.AnimationOptions | undefined) => motion_dom.AnimationPlaybackControlsWithThen]; + +export { useAnimateMini as useAnimate }; diff --git a/node_modules/framer-motion/dist/mini.js b/node_modules/framer-motion/dist/mini.js new file mode 100644 index 00000000..8f02b501 --- /dev/null +++ b/node_modules/framer-motion/dist/mini.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Motion={},t.React)}(this,function(t,e){"use strict";function i(t){const i=e.useRef(null);return null===i.current&&(i.current=t()),i.current}function n(t){let e;return()=>(void 0===e&&(e=t()),e)}const s=t=>t,a=t=>1e3*t,o=t=>t/1e3,r=t=>null!==t;class l{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}function u(t){for(let e=1;et.startsWith("--");const d=n(()=>void 0!==window.ScrollTimeline),m={};function c(t,e){const i=n(t);return()=>m[e]??i()}const p=c(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),f=([t,e,i,n])=>`cubic-bezier(${t}, ${e}, ${i}, ${n})`,y={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:f([0,.65,.55,1]),circOut:f([.55,0,1,.45]),backIn:f([.31,.01,.66,-.59]),backOut:f([.33,1.53,.69,.99])};function g(t,e){return t?"function"==typeof t?p()?((t,e,i=10)=>{let n="";const s=Math.max(Math.round(e/i),2);for(let e=0;eArray.isArray(t)&&"number"==typeof t[0])(t)?f(t):Array.isArray(t)?t.map(t=>g(t,e)||y.easeOut):y[t]:void 0}class T extends l{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:i,keyframes:n,pseudoElement:s,allowFlatten:a=!1,finalKeyframe:o,onComplete:l}=t;this.isPseudoElement=Boolean(s),this.allowFlatten=a,this.options=t,t.type;const u=function({type:t,...e}){return function(t){return"function"==typeof t&&"applyToOptions"in t}(t)&&p()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=function(t,e,i,{delay:n=0,duration:s=300,repeat:a=0,repeatType:o="loop",ease:r="easeOut",times:l}={},u){const h={[e]:i};l&&(h.offset=l);const d=g(r,s);Array.isArray(d)&&(h.easing=d);const m={delay:n,duration:s,easing:Array.isArray(d)?"linear":d,fill:"both",iterations:a+1,direction:"reverse"===o?"alternate":"normal"};return u&&(m.pseudoElement=u),t.animate(h,m)}(e,i,n,u,s),!1===u.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!s){const t=function(t,{repeat:e,repeatType:i="loop"},n,s=1){const a=t.filter(r),o=s<0||e&&"loop"!==i&&e%2==1?0:a.length-1;return o&&void 0!==n?n:a[o]}(n,this.options,o,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,i){h(e)?t.style.setProperty(e,i):t.style[e]=i}(e,i,t),this.animation.cancel()}l?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return o(Number(t))}get time(){return o(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=a(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&d()?(this.animation.timeline=t,s):e(this)}}class b{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let i=0;ie.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class A extends b{then(t,e){return this.finished.finally(t).then(()=>{})}}const w=new WeakMap,E=(t,e="")=>`${t}:${e}`;function R(t){const e=w.get(t)||new Map;return w.set(t,e),e}function v(t,e){return t?.[e]??t?.default??t}const S=new Set(["borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderRadius","radius","borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius","width","maxWidth","height","maxHeight","top","right","bottom","left","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","backgroundPositionX","backgroundPositionY"]);function k(t,e){for(let i=0;ie.delete(i)),l.push(s)}return l}t.useAnimate=function(){const t=i(()=>({current:null,animations:[]})),n=i(()=>(t=>function(e,i,n){return new A(x(e,i,n,t))})(t));var s;return s=()=>{t.animations.forEach(t=>t.stop())},e.useEffect(()=>()=>s(),[]),[t,n]}}); diff --git a/node_modules/framer-motion/dist/size-rollup-animate.js b/node_modules/framer-motion/dist/size-rollup-animate.js new file mode 100644 index 00000000..1b0aef4c --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-animate.js @@ -0,0 +1 @@ +function t(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}const e=(t,e,n)=>n>e?e:n/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t);function i(t){return"object"==typeof t&&null!==t}const r=t=>/^0[^.\s]+$/u.test(t);function a(t){let e;return()=>(void 0===e&&(e=t()),e)}const o=t=>t,l=(t,e)=>n=>e(t(n)),u=(...t)=>t.reduce(l),h=(t,e,n)=>{const s=e-t;return 0===s?1:(n-t)/s};class c{constructor(){this.subscriptions=[]}add(e){var n,s;return n=this.subscriptions,s=e,-1===n.indexOf(s)&&n.push(s),()=>t(this.subscriptions,e)}notify(t,e,n){const s=this.subscriptions.length;if(s)if(1===s)this.subscriptions[0](t,e,n);else for(let i=0;i1e3*t,d=t=>t/1e3;function m(t,e){return e?t*(1e3/e):0}const f=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function y(t,e,n,s){if(t===e&&n===s)return o;const i=e=>function(t,e,n,s,i){let r,a,o=0;do{a=e+(n-e)/2,r=f(a,s,i)-t,r>0?n=a:e=a}while(Math.abs(r)>1e-7&&++o<12);return a}(e,0,1,t,n);return t=>0===t||1===t?t:f(i(t),e,s)}const g=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,v=t=>e=>1-t(1-e),b=y(.33,1.53,.69,.99),w=v(b),T=g(w),M=t=>(t*=2)<1?.5*w(t):.5*(2-Math.pow(2,-10*(t-1))),V=t=>1-Math.sin(Math.acos(t)),A=v(V),S=g(V),x=y(.42,0,1,1),k=y(0,0,.58,1),C=y(.42,0,.58,1),F=t=>Array.isArray(t)&&"number"!=typeof t[0];function P(t,e){return F(t)?t[((t,e,n)=>{const s=e-t;return((n-t)%s+s)%s+t})(0,t.length,e)]:t}const E=t=>Array.isArray(t)&&"number"==typeof t[0],O={linear:o,easeIn:x,easeInOut:C,easeOut:k,circIn:V,circInOut:S,circOut:A,backIn:w,backInOut:T,backOut:b,anticipate:M},D=t=>{if(E(t)){t.length;const[e,n,s,i]=t;return y(e,n,s,i)}return"string"==typeof t?O[t]:t},I=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],R={value:null,addProjectionMetrics:null};function B(t,e){let s=!1,i=!0;const r={delta:0,timestamp:0,isProcessing:!1},a=()=>s=!0,o=I.reduce((t,n)=>(t[n]=function(t,e){let n=new Set,s=new Set,i=!1,r=!1;const a=new WeakSet;let o={delta:0,timestamp:0,isProcessing:!1},l=0;function u(e){a.has(e)&&(h.schedule(e),t()),l++,e(o)}const h={schedule:(t,e=!1,r=!1)=>{const o=r&&i?n:s;return e&&a.add(t),o.has(t)||o.add(t),t},cancel:t=>{s.delete(t),a.delete(t)},process:t=>{o=t,i?r=!0:(i=!0,[n,s]=[s,n],n.forEach(u),e&&R.value&&R.value.frameloop[e].push(l),l=0,n.clear(),i=!1,r&&(r=!1,h.process(t)))}};return h}(a,e?n:void 0),t),{}),{setup:l,read:u,resolveKeyframes:h,preUpdate:c,update:p,preRender:d,render:m,postRender:f}=o,y=()=>{const a=n.useManualTiming?r.timestamp:performance.now();s=!1,n.useManualTiming||(r.delta=i?1e3/60:Math.max(Math.min(a-r.timestamp,40),1)),r.timestamp=a,r.isProcessing=!0,l.process(r),u.process(r),h.process(r),c.process(r),p.process(r),d.process(r),m.process(r),f.process(r),r.isProcessing=!1,s&&e&&(i=!1,t(y))};return{schedule:I.reduce((e,n)=>{const a=o[n];return e[n]=(e,n=!1,o=!1)=>(s||(s=!0,i=!0,r.isProcessing||t(y)),a.schedule(e,n,o)),e},{}),cancel:t=>{for(let e=0;e(void 0===Y&&L.set(j.isProcessing||n.useManualTiming?j.timestamp:performance.now()),Y),set:t=>{Y=t,queueMicrotask($)}},U=t=>e=>"string"==typeof e&&e.startsWith(t),X=U("--"),q=U("var(--"),z=t=>!!q(t)&&Z.test(t.split("/*")[0].trim()),Z=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,H={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},_={...H,transform:t=>e(0,1,t)},G={...H,default:1},J=t=>Math.round(1e5*t)/1e5,Q=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const tt=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,et=(t,e)=>n=>Boolean("string"==typeof n&&tt.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),nt=(t,e,n)=>s=>{if("string"!=typeof s)return s;const[i,r,a,o]=s.match(Q);return{[t]:parseFloat(i),[e]:parseFloat(r),[n]:parseFloat(a),alpha:void 0!==o?parseFloat(o):1}},st={...H,transform:t=>Math.round((t=>e(0,255,t))(t))},it={test:et("rgb","red"),parse:nt("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:s=1})=>"rgba("+st.transform(t)+", "+st.transform(e)+", "+st.transform(n)+", "+J(_.transform(s))+")"};const rt={test:et("#"),parse:function(t){let e="",n="",s="",i="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),s=t.substring(5,7),i=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),s=t.substring(3,4),i=t.substring(4,5),e+=e,n+=n,s+=s,i+=i),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(s,16),alpha:i?parseInt(i,16)/255:1}},transform:it.transform},at=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),ot=at("deg"),lt=at("%"),ut=at("px"),ht=at("vh"),ct=at("vw"),pt=(()=>({...lt,parse:t=>lt.parse(t)/100,transform:t=>lt.transform(100*t)}))(),dt={test:et("hsl","hue"),parse:nt("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:s=1})=>"hsla("+Math.round(t)+", "+lt.transform(J(e))+", "+lt.transform(J(n))+", "+J(_.transform(s))+")"},mt={test:t=>it.test(t)||rt.test(t)||dt.test(t),parse:t=>it.test(t)?it.parse(t):dt.test(t)?dt.parse(t):rt.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?it.transform(t):dt.transform(t),getAnimatableNone:t=>{const e=mt.parse(t);return e.alpha=0,mt.transform(e)}},ft=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const yt="number",gt="color",vt=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function bt(t){const e=t.toString(),n=[],s={color:[],number:[],var:[]},i=[];let r=0;const a=e.replace(vt,t=>(mt.test(t)?(s.color.push(r),i.push(gt),n.push(mt.parse(t))):t.startsWith("var(")?(s.var.push(r),i.push("var"),n.push(t)):(s.number.push(r),i.push(yt),n.push(parseFloat(t))),++r,"${}")).split("${}");return{values:n,split:a,indexes:s,types:i}}function wt(t){return bt(t).values}function Tt(t){const{split:e,types:n}=bt(t),s=e.length;return t=>{let i="";for(let r=0;r"number"==typeof t?0:mt.test(t)?mt.getAnimatableNone(t):t;const Vt={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(Q)?.length||0)+(t.match(ft)?.length||0)>0},parse:wt,createTransformer:Tt,getAnimatableNone:function(t){const e=wt(t);return Tt(t)(e.map(Mt))}};function At(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function St(t,e){return n=>n>0?e:t}const xt=(t,e,n)=>t+(e-t)*n,kt=(t,e,n)=>{const s=t*t,i=n*(e*e-s)+s;return i<0?0:Math.sqrt(i)},Ct=[rt,it,dt];function Ft(t){const e=(n=t,Ct.find(t=>t.test(n)));var n;if(!Boolean(e))return!1;let s=e.parse(t);return e===dt&&(s=function({hue:t,saturation:e,lightness:n,alpha:s}){t/=360,n/=100;let i=0,r=0,a=0;if(e/=100){const s=n<.5?n*(1+e):n+e-n*e,o=2*n-s;i=At(o,s,t+1/3),r=At(o,s,t),a=At(o,s,t-1/3)}else i=r=a=n;return{red:Math.round(255*i),green:Math.round(255*r),blue:Math.round(255*a),alpha:s}}(s)),s}const Pt=(t,e)=>{const n=Ft(t),s=Ft(e);if(!n||!s)return St(t,e);const i={...n};return t=>(i.red=kt(n.red,s.red,t),i.green=kt(n.green,s.green,t),i.blue=kt(n.blue,s.blue,t),i.alpha=xt(n.alpha,s.alpha,t),it.transform(i))},Et=new Set(["none","hidden"]);function Ot(t,e){return n=>xt(t,e,n)}function Dt(t){return"number"==typeof t?Ot:"string"==typeof t?z(t)?St:mt.test(t)?Pt:Bt:Array.isArray(t)?It:"object"==typeof t?mt.test(t)?Pt:Rt:St}function It(t,e){const n=[...t],s=n.length,i=t.map((t,n)=>Dt(t)(t,e[n]));return t=>{for(let e=0;e{for(const e in s)n[e]=s[e](t);return n}}const Bt=(t,e)=>{const n=Vt.createTransformer(e),s=bt(t),i=bt(e);return s.indexes.var.length===i.indexes.var.length&&s.indexes.color.length===i.indexes.color.length&&s.indexes.number.length>=i.indexes.number.length?Et.has(t)&&!i.values.length||Et.has(e)&&!s.values.length?function(t,e){return Et.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}(t,e):u(It(function(t,e){const n=[],s={color:0,var:0,number:0};for(let i=0;i{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>N.update(e,t),stop:()=>K(e),now:()=>j.isProcessing?j.timestamp:L.now()}},jt=(t,e,n=10)=>{let s="";const i=Math.max(Math.round(e/n),2);for(let e=0;e=Wt?1/0:e}function $t(t,e=100,n){const s=n({...t,keyframes:[0,e]}),i=Math.min(Yt(s),Wt);return{type:"keyframes",ease:t=>s.next(i*t).value/e,duration:d(i)}}function Lt(t,e,n){const s=Math.max(e-5,0);return m(n-t(s),e-s)}const Ut=100,Xt=10,qt=1,zt=0,Zt=800,Ht=.3,_t=.3,Gt={granular:.01,default:2},Jt={granular:.005,default:.5},Qt=.01,te=10,ee=.05,ne=1,se=.001;function ie({duration:t=Zt,bounce:n=Ht,velocity:s=zt,mass:i=qt}){let r,a,o=1-n;o=e(ee,ne,o),t=e(Qt,te,d(t)),o<1?(r=e=>{const n=e*o,i=n*t,r=n-s,a=ae(e,o),l=Math.exp(-i);return se-r/a*l},a=e=>{const n=e*o*t,i=n*s+s,a=Math.pow(o,2)*Math.pow(e,2)*t,l=Math.exp(-n),u=ae(Math.pow(e,2),o);return(-r(e)+se>0?-1:1)*((i-a)*l)/u}):(r=e=>Math.exp(-e*t)*((e-s)*t+1)-.001,a=e=>Math.exp(-e*t)*(t*t*(s-e)));const l=function(t,e,n){let s=n;for(let n=1;nvoid 0!==t[e])}function he(t=_t,n=Ht){const s="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:n}:t;let{restSpeed:i,restDelta:r}=s;const a=s.keyframes[0],o=s.keyframes[s.keyframes.length-1],l={done:!1,value:a},{stiffness:u,damping:h,mass:c,duration:m,velocity:f,isResolvedFromDuration:y}=function(t){let n={velocity:zt,stiffness:Ut,damping:Xt,mass:qt,isResolvedFromDuration:!1,...t};if(!ue(t,le)&&ue(t,oe))if(t.visualDuration){const s=t.visualDuration,i=2*Math.PI/(1.2*s),r=i*i,a=2*e(.05,1,1-(t.bounce||0))*Math.sqrt(r);n={...n,mass:qt,stiffness:r,damping:a}}else{const e=ie(t);n={...n,...e,mass:qt},n.isResolvedFromDuration=!0}return n}({...s,velocity:-d(s.velocity||0)}),g=f||0,v=h/(2*Math.sqrt(u*c)),b=o-a,w=d(Math.sqrt(u/c)),T=Math.abs(b)<5;let M;if(i||(i=T?Gt.granular:Gt.default),r||(r=T?Jt.granular:Jt.default),v<1){const t=ae(w,v);M=e=>{const n=Math.exp(-v*w*e);return o-n*((g+v*w*b)/t*Math.sin(t*e)+b*Math.cos(t*e))}}else if(1===v)M=t=>o-Math.exp(-w*t)*(b+(g+w*b)*t);else{const t=w*Math.sqrt(v*v-1);M=e=>{const n=Math.exp(-v*w*e),s=Math.min(t*e,300);return o-n*((g+v*w*b)*Math.sinh(s)+t*b*Math.cosh(s))/t}}const V={calculatedDuration:y&&m||null,next:t=>{const e=M(t);if(y)l.done=t>=m;else{let n=0===t?g:0;v<1&&(n=0===t?p(g):Lt(M,t,e));const s=Math.abs(n)<=i,a=Math.abs(o-e)<=r;l.done=s&&a}return l.value=l.done?o:e,l},toString:()=>{const t=Math.min(Yt(V),Wt),e=jt(e=>V.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return V}function ce({keyframes:t,velocity:e=0,power:n=.8,timeConstant:s=325,bounceDamping:i=10,bounceStiffness:r=500,modifyTarget:a,min:o,max:l,restDelta:u=.5,restSpeed:h}){const c=t[0],p={done:!1,value:c},d=t=>void 0===o?l:void 0===l||Math.abs(o-t)-m*Math.exp(-t/s),v=t=>y+g(t),b=t=>{const e=g(t),n=v(t);p.done=Math.abs(e)<=u,p.value=p.done?y:n};let w,T;const M=t=>{var e;(e=p.value,void 0!==o&&el)&&(w=t,T=he({keyframes:[p.value,d(p.value)],velocity:Lt(v,t,p.value),damping:i,stiffness:r,restDelta:u,restSpeed:h}))};return M(0),{calculatedDuration:null,next:t=>{let e=!1;return T||void 0!==w||(e=!0,b(t),M(t)),void 0!==w&&t>=w?T.next(t-w):(!e&&b(t),p)}}}function pe(t,s,{clamp:i=!0,ease:r,mixer:a}={}){const l=t.length;if(s.length,1===l)return()=>s[0];if(2===l&&s[0]===s[1])return()=>s[1];const c=t[0]===t[1];t[0]>t[l-1]&&(t=[...t].reverse(),s=[...s].reverse());const p=function(t,e,s){const i=[],r=s||n.mix||Nt,a=t.length-1;for(let n=0;n{if(c&&e1)for(;nm(e(t[0],t[l-1],n)):m}function de(t,e){const n=t[t.length-1];for(let s=1;s<=e;s++){const i=h(0,e,s);t.push(xt(n,1,i))}}function me(t){const e=[0];return de(e,t.length-1),e}function fe({duration:t=300,keyframes:e,times:n,ease:s="easeInOut"}){const i=F(s)?s.map(D):D(s),r={done:!1,value:e[0]},a=function(t,e){return t.map(t=>t*e)}(n&&n.length===e.length?n:me(e),t),o=pe(a,e,{ease:Array.isArray(i)?i:(l=e,u=i,l.map(()=>u||C).splice(0,l.length-1))});var l,u;return{calculatedDuration:t,next:e=>(r.value=o(e),r.done=e>=t,r)}}he.applyToOptions=t=>{const e=$t(t,100,he);return t.ease=e.ease,t.duration=p(e.duration),t.type="keyframes",t};const ye=t=>null!==t;function ge(t,{repeat:e,repeatType:n="loop"},s,i=1){const r=t.filter(ye),a=i<0||e&&"loop"!==n&&e%2==1?0:r.length-1;return a&&void 0!==s?s:r[a]}const ve={decay:ce,inertia:ce,tween:fe,keyframes:fe,spring:he};function be(t){"string"==typeof t.type&&(t.type=ve[t.type])}class we{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Te=t=>t/100;class Me extends we{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==L.now()&&this.tick(L.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;be(t);const{type:e=fe,repeat:n=0,repeatDelay:s=0,repeatType:i,velocity:r=0}=t;let{keyframes:a}=t;const o=e||fe;o!==fe&&"number"!=typeof a[0]&&(this.mixKeyframes=u(Te,Nt(a[0],a[1])),a=[0,100]);const l=o({...t,keyframes:a});"mirror"===i&&(this.mirroredGenerator=o({...t,keyframes:[...a].reverse(),velocity:-r})),null===l.calculatedDuration&&(l.calculatedDuration=Yt(l));const{calculatedDuration:h}=l;this.calculatedDuration=h,this.resolvedDuration=h+s,this.totalDuration=this.resolvedDuration*(n+1)-s,this.generator=l}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,n=!1){const{generator:s,totalDuration:i,mixKeyframes:r,mirroredGenerator:a,resolvedDuration:o,calculatedDuration:l}=this;if(null===this.startTime)return s.next(0);const{delay:u=0,keyframes:h,repeat:c,repeatType:p,repeatDelay:d,type:m,onUpdate:f,finalKeyframe:y}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-i/this.speed,this.startTime)),n?this.currentTime=t:this.updateTime(t);const g=this.currentTime-u*(this.playbackSpeed>=0?1:-1),v=this.playbackSpeed>=0?g<0:g>i;this.currentTime=Math.max(g,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=i);let b=this.currentTime,w=s;if(c){const t=Math.min(this.currentTime,i)/o;let n=Math.floor(t),s=t%1;!s&&t>=1&&(s=1),1===s&&n--,n=Math.min(n,c+1);Boolean(n%2)&&("reverse"===p?(s=1-s,d&&(s-=d/o)):"mirror"===p&&(w=a)),b=e(0,1,s)*o}const T=v?{done:!1,value:h[0]}:w.next(b);r&&(T.value=r(T.value));let{done:M}=T;v||null===l||(M=this.playbackSpeed>=0?this.currentTime>=i:this.currentTime<=0);const V=null===this.holdTime&&("finished"===this.state||"running"===this.state&&M);return V&&m!==ce&&(T.value=ge(h,this.options,y,this.speed)),f&&f(T.value),V&&this.finish(),T}then(t,e){return this.finished.then(t,e)}get duration(){return d(this.calculatedDuration)}get time(){return d(this.currentTime)}set time(t){t=p(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(L.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=d(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=Kt,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const n=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=n):null!==this.holdTime?this.startTime=n-this.holdTime:this.startTime||(this.startTime=e??n),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(L.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}const Ve=t=>180*t/Math.PI,Ae=t=>{const e=Ve(Math.atan2(t[1],t[0]));return xe(e)},Se={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:Ae,rotateZ:Ae,skewX:t=>Ve(Math.atan(t[1])),skewY:t=>Ve(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},xe=t=>((t%=360)<0&&(t+=360),t),ke=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),Ce=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),Fe={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:ke,scaleY:Ce,scale:t=>(ke(t)+Ce(t))/2,rotateX:t=>xe(Ve(Math.atan2(t[6],t[5]))),rotateY:t=>xe(Ve(Math.atan2(-t[2],t[0]))),rotateZ:Ae,rotate:Ae,skewX:t=>Ve(Math.atan(t[4])),skewY:t=>Ve(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function Pe(t){return t.includes("scale")?1:0}function Ee(t,e){if(!t||"none"===t)return Pe(e);const n=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let s,i;if(n)s=Fe,i=n;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);s=Se,i=e}if(!i)return Pe(e);const r=s[e],a=i[1].split(",").map(Oe);return"function"==typeof r?r(a):a[r]}function Oe(t){return parseFloat(t.trim())}const De=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],Ie=(()=>new Set(De))(),Re=t=>t===H||t===ut,Be=new Set(["x","y","z"]),Ne=De.filter(t=>!Be.has(t));const Ke={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>Ee(e,"x"),y:(t,{transform:e})=>Ee(e,"y")};Ke.translateX=Ke.x,Ke.translateY=Ke.y;const je=new Set;let We=!1,Ye=!1,$e=!1;function Le(){if(Ye){const t=Array.from(je).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return Ne.forEach(n=>{const s=t.getValue(n);void 0!==s&&(e.push([n,s.get()]),s.set(n.startsWith("scale")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{t.getValue(e)?.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}Ye=!1,We=!1,je.forEach(t=>t.complete($e)),je.clear()}function Ue(){je.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(Ye=!0)})}class Xe{constructor(t,e,n,s,i,r=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=s,this.element=i,this.isAsync=r}scheduleResolve(){this.state="scheduled",this.isAsync?(je.add(this),We||(We=!0,N.read(Ue),N.resolveKeyframes(Le))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:s}=this;if(null===t[0]){const i=s?.get(),r=t[t.length-1];if(void 0!==i)t[0]=i;else if(n&&e){const s=n.readValue(e,r);null!=s&&(t[0]=s)}void 0===t[0]&&(t[0]=r),s&&void 0===i&&s.set(t[0])}!function(t){for(let e=1;evoid 0!==window.ScrollTimeline),ze={};function Ze(t,e){const n=a(t);return()=>ze[e]??n()}const He=Ze(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),_e=([t,e,n,s])=>`cubic-bezier(${t}, ${e}, ${n}, ${s})`,Ge={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:_e([0,.65,.55,1]),circOut:_e([.55,0,1,.45]),backIn:_e([.31,.01,.66,-.59]),backOut:_e([.33,1.53,.69,.99])};function Je(t,e){return t?"function"==typeof t?He()?jt(t,e):"ease-out":E(t)?_e(t):Array.isArray(t)?t.map(t=>Je(t,e)||Ge.easeOut):Ge[t]:void 0}function Qe(t,e,n,{delay:s=0,duration:i=300,repeat:r=0,repeatType:a="loop",ease:o="easeOut",times:l}={},u=void 0){const h={[e]:n};l&&(h.offset=l);const c=Je(o,i);Array.isArray(c)&&(h.easing=c);const p={delay:s,duration:i,easing:Array.isArray(c)?"linear":c,fill:"both",iterations:r+1,direction:"reverse"===a?"alternate":"normal"};u&&(p.pseudoElement=u);return t.animate(h,p)}function tn(t){return"function"==typeof t&&"applyToOptions"in t}class en extends we{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:n,keyframes:s,pseudoElement:i,allowFlatten:r=!1,finalKeyframe:a,onComplete:o}=t;this.isPseudoElement=Boolean(i),this.allowFlatten=r,this.options=t,t.type;const l=function({type:t,...e}){return tn(t)&&He()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=Qe(e,n,s,l,i),!1===l.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!i){const t=ge(s,this.options,a,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,n){(t=>t.startsWith("--"))(e)?t.style.setProperty(e,n):t.style[e]=n}(e,n,t),this.animation.cancel()}o?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return d(Number(t))}get time(){return d(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=p(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&qe()?(this.animation.timeline=t,o):e(this)}}const nn={anticipate:M,backInOut:T,circInOut:S};function sn(t){"string"==typeof t.ease&&t.ease in nn&&(t.ease=nn[t.ease])}class rn extends en{constructor(t){sn(t),be(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:n,onComplete:s,element:i,...r}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const a=new Me({...r,autoplay:!1}),o=p(this.finishedTime??this.time);e.setWithVelocity(a.sample(o-10).value,a.sample(o).value,10),a.stop()}}const an=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!Vt.test(t)&&"0"!==t||t.startsWith("url(")));const on=new Set(["opacity","clipPath","filter","transform"]),ln=a(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));function un(t){const{motionValue:e,name:n,repeatDelay:s,repeatType:r,damping:a,type:o}=t;if(l=e?.owner?.current,!i(l)||!("offsetHeight"in l))return!1;var l;const{onUpdate:u,transformTemplate:h}=e.owner.getProps();return ln()&&n&&on.has(n)&&("transform"!==n||!h)&&!u&&!s&&"mirror"!==r&&0!==a&&"inertia"!==o}class hn extends we{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:s=0,repeatDelay:i=0,repeatType:r="loop",keyframes:a,name:o,motionValue:l,element:u,...h}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=L.now();const c={autoplay:t,delay:e,type:n,repeat:s,repeatDelay:i,repeatType:r,name:o,motionValue:l,element:u,...h},p=u?.KeyframeResolver||Xe;this.keyframeResolver=new p(a,(t,e,n)=>this.onKeyframesResolved(t,e,c,!n),o,l,u),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,e,s,i){this.keyframeResolver=void 0;const{name:r,type:a,velocity:l,delay:u,isHandoff:h,onUpdate:c}=s;this.resolvedAt=L.now(),function(t,e,n,s){const i=t[0];if(null===i)return!1;if("display"===e||"visibility"===e)return!0;const r=t[t.length-1],a=an(i,e),o=an(r,e);return!(!a||!o)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:e,...s,keyframes:t},d=!h&&un(p)?new rn({...p,element:p.motionValue.owner.current}):new Me(p);d.finished.then(()=>this.notifyFinished()).catch(o),this.pendingTimeline&&(this.stopTimeline=d.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=d}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),$e=!0,Ue(),Le(),$e=!1),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}}class cn{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let n=0;ne.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class pn extends cn{then(t,e){return this.finished.finally(t).then(()=>{})}}const dn=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function mn(t,e,n=1){const[i,r]=function(t){const e=dn.exec(t);if(!e)return[,];const[,n,s,i]=e;return[`--${n??s}`,i]}(t);if(!i)return;const a=window.getComputedStyle(e).getPropertyValue(i);if(a){const t=a.trim();return s(t)?parseFloat(t):t}return z(r)?mn(r,e,n+1):r}function fn(t,e){return t?.[e]??t?.default??t}const yn=new Set(["width","height","top","left","right","bottom",...De]),gn=t=>e=>e.test(t),vn=[H,ut,lt,ot,ct,ht,{test:t=>"auto"===t,parse:t=>t}],bn=t=>vn.find(gn(t));function wn(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||r(t))}const Tn=new Set(["brightness","contrast","saturate","opacity"]);function Mn(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[s]=n.match(Q)||[];if(!s)return t;const i=n.replace(s,"");let r=Tn.has(e)?1:0;return s!==n&&(r*=100),e+"("+r+i+")"}const Vn=/\b([a-z-]*)\(.*?\)/gu,An={...Vt,getAnimatableNone:t=>{const e=t.match(Vn);return e?e.map(Mn).join(" "):t}},Sn={...H,transform:Math.round},xn={borderWidth:ut,borderTopWidth:ut,borderRightWidth:ut,borderBottomWidth:ut,borderLeftWidth:ut,borderRadius:ut,radius:ut,borderTopLeftRadius:ut,borderTopRightRadius:ut,borderBottomRightRadius:ut,borderBottomLeftRadius:ut,width:ut,maxWidth:ut,height:ut,maxHeight:ut,top:ut,right:ut,bottom:ut,left:ut,padding:ut,paddingTop:ut,paddingRight:ut,paddingBottom:ut,paddingLeft:ut,margin:ut,marginTop:ut,marginRight:ut,marginBottom:ut,marginLeft:ut,backgroundPositionX:ut,backgroundPositionY:ut,...{rotate:ot,rotateX:ot,rotateY:ot,rotateZ:ot,scale:G,scaleX:G,scaleY:G,scaleZ:G,skew:ot,skewX:ot,skewY:ot,distance:ut,translateX:ut,translateY:ut,translateZ:ut,x:ut,y:ut,z:ut,perspective:ut,transformPerspective:ut,opacity:_,originX:pt,originY:pt,originZ:ut},zIndex:Sn,fillOpacity:_,strokeOpacity:_,numOctaves:Sn},kn={...xn,color:mt,backgroundColor:mt,outlineColor:mt,fill:mt,stroke:mt,borderColor:mt,borderTopColor:mt,borderRightColor:mt,borderBottomColor:mt,borderLeftColor:mt,filter:An,WebkitFilter:An},Cn=t=>kn[t];function Fn(t,e){let n=Cn(t);return n!==An&&(n=Vt),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const Pn=new Set(["auto","none","0"]);class En extends Xe{constructor(t,e,n,s,i){super(t,e,n,s,i,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{t.getValue(e).set(n)}),this.resolveNoneKeyframes()}}const On=(t,e)=>e&&"number"==typeof t?e.transform(t):t;class Dn{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const n=L.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=L.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new c);const n=this.events[t].add(e);return"change"===t?()=>{n(),N.read(()=>{this.events.change.getSize()||this.stop()})}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=L.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return m(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function In(t,e){return new Dn(t,e)}function Rn(t){return i(t)&&"ownerSVGElement"in t}const Bn=t=>Boolean(t&&t.getVelocity),Nn=[...vn,mt,Vt];function Kn(t){return"object"==typeof t&&!Array.isArray(t)}function jn(t,e,n,s){return"string"==typeof t&&Kn(e)?function(t,e,n){if(t instanceof EventTarget)return[t];if("string"==typeof t){let s=document;e&&(s=e.current);const i=n?.[t]??s.querySelectorAll(t);return i?Array.from(i):[]}return Array.from(t)}(t,n,s):t instanceof NodeList?Array.from(t):Array.isArray(t)?t:[t]}function Wn(t,e,n){return t*(e+1)}function Yn(t,e,n,s){return"number"==typeof e?e:e.startsWith("-")||e.startsWith("+")?Math.max(0,t+parseFloat(e)):"<"===e?n:e.startsWith("<")?Math.max(0,n+parseFloat(e.slice(1))):s.get(e)??t}function $n(e,n,s,i,r,a){!function(e,n,s){for(let i=0;in&&r.at"number"==typeof t,_n=t=>t.every(Hn),Gn=new WeakMap;function Jn(t){const e=[{},{}];return t?.values.forEach((t,n)=>{e[0][n]=t.get(),e[1][n]=t.getVelocity()}),e}function Qn(t,e,n,s){if("function"==typeof e){const[i,r]=Jn(s);e=e(void 0!==n?n:t.custom,i,r)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[i,r]=Jn(s);e=e(void 0!==n?n:t.custom,i,r)}return e}function ts(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,In(n))}function es(t){return(t=>Array.isArray(t))(t)?t[t.length-1]||0:t}function ns(t,e){const n=function(t,e,n){const s=t.getProps();return Qn(s,e,void 0!==n?n:s.custom,t)}(t,e);let{transitionEnd:s={},transition:i={},...r}=n||{};r={...r,...s};for(const e in r){ts(t,e,es(r[e]))}}function ss(t,e){const s=t.getValue("willChange");if(i=s,Boolean(Bn(i)&&i.add))return s.add(e);if(!s&&n.WillChange){const s=new n.WillChange("auto");t.addValue("willChange",s),s.add(e)}var i}const is=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),rs="data-"+is("framerAppearId");function as(t){return t.props[rs]}const os=t=>null!==t;const ls={type:"spring",stiffness:500,damping:25,restSpeed:10},us={type:"keyframes",duration:.8},hs={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},cs=(t,{keyframes:e})=>e.length>2?us:Ie.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:ls:hs;const ps=(t,e,s,i={},r,a)=>o=>{const l=fn(i,t)||{},u=l.delay||i.delay||0;let{elapsed:h=0}=i;h-=p(u);const c={keyframes:Array.isArray(s)?s:[null,s],ease:"easeOut",velocity:e.getVelocity(),...l,delay:-h,onUpdate:t=>{e.set(t),l.onUpdate&&l.onUpdate(t)},onComplete:()=>{o(),l.onComplete&&l.onComplete()},name:t,motionValue:e,element:a?void 0:r};(function({when:t,delay:e,delayChildren:n,staggerChildren:s,staggerDirection:i,repeat:r,repeatType:a,repeatDelay:o,from:l,elapsed:u,...h}){return!!Object.keys(h).length})(l)||Object.assign(c,cs(t,c)),c.duration&&(c.duration=p(c.duration)),c.repeatDelay&&(c.repeatDelay=p(c.repeatDelay)),void 0!==c.from&&(c.keyframes[0]=c.from);let d=!1;if((!1===c.type||0===c.duration&&!c.repeatDelay)&&(c.duration=0,0===c.delay&&(d=!0)),(n.instantAnimations||n.skipAnimations)&&(d=!0,c.duration=0,c.delay=0),c.allowFlatten=!l.type&&!l.ease,d&&!a&&void 0!==e.get()){const t=function(t,{repeat:e,repeatType:n="loop"},s){const i=t.filter(os),r=e&&"loop"!==n&&e%2==1?0:i.length-1;return r&&void 0!==s?s:i[r]}(c.keyframes,l);if(void 0!==t)return void N.update(()=>{c.onUpdate(t),c.onComplete()})}return l.isSync?new Me(c):new hn(c)};function ds({protectedKeys:t,needsAnimating:e},n){const s=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,s}function ms(t,e,{delay:n=0,transitionOverride:s,type:i}={}){let{transition:r=t.getDefaultTransition(),transitionEnd:a,...o}=e;s&&(r=s);const l=[],u=i&&t.animationState&&t.animationState.getState()[i];for(const e in o){const s=t.getValue(e,t.latestValues[e]??null),i=o[e];if(void 0===i||u&&ds(u,e))continue;const a={delay:n,...fn(r||{},e)},h=s.get();if(void 0!==h&&!s.isAnimating&&!Array.isArray(i)&&i===h&&!a.velocity)continue;let c=!1;if(window.MotionHandoffAnimation){const n=as(t);if(n){const t=window.MotionHandoffAnimation(n,e,N);null!==t&&(a.startTime=t,c=!0)}}ss(t,e),s.start(ps(e,s,i,t.shouldReduceMotion&&yn.has(e)?{type:!1}:a,t,c));const p=s.animation;p&&l.push(p)}return a&&Promise.all(l).then(()=>{N.update(()=>{a&&ns(t,a)})}),l}const fs={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},ys={};for(const t in fs)ys[t]={isEnabled:e=>fs[t].some(t=>!!e[t])};const gs=()=>({x:{min:0,max:0},y:{min:0,max:0}}),vs="undefined"!=typeof window,bs={current:null},ws={current:!1};const Ts=["initial","animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"];function Ms(t){return null!==(e=t.animate)&&"object"==typeof e&&"function"==typeof e.start||Ts.some(e=>function(t){return"string"==typeof t||Array.isArray(t)}(t[e]));var e}const Vs=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class As{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:s,blockInitialAnimation:i,visualState:r},a={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=Xe,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=L.now();this.renderScheduledAtthis.bindToMotionValue(e,t)),ws.current||function(){if(ws.current=!0,vs)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>bs.current=t.matches;t.addEventListener("change",e),e()}else bs.current=!1}(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||bs.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){this.projection&&this.projection.unmount(),K(this.notifyUpdate),K(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=Ie.has(t);n&&this.onBindTransform&&this.onBindTransform();const s=e.on("change",e=>{this.latestValues[t]=e,this.props.onUpdate&&N.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0)}),i=e.on("renderRequest",this.scheduleRender);let r;window.MotionCheckAppearSync&&(r=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{s(),i(),r&&r(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in ys){const e=ys[t];if(!e)continue;const{isEnabled:n,Feature:s}=e;if(!this.features[t]&&s&&n(this.props)&&(this.features[t]=new s(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=In(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){let n=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];var i;return null!=n&&("string"==typeof n&&(s(n)||r(n))?n=parseFloat(n):(i=n,!Nn.find(gn(i))&&Vt.test(e)&&(n=Fn(t,e))),this.setBaseTarget(t,Bn(n)?n.get():n)),Bn(n)?n.get():n}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){const{initial:e}=this.props;let n;if("string"==typeof e||"object"==typeof e){const s=Qn(this.props,e,this.presenceContext?.custom);s&&(n=s[t])}if(e&&void 0!==n)return n;const s=this.getBaseTargetFromProps(this.props,t);return void 0===s||Bn(s)?void 0!==this.initialValues[t]&&void 0===n?void 0:this.baseTarget[t]:s}on(t,e){return this.events[t]||(this.events[t]=new c),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class Ss extends As{constructor(){super(...arguments),this.KeyframeResolver=En}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;Bn(t)&&(this.childSubscription=t.on("change",t=>{this.current&&(this.current.textContent=`${t}`)}))}}const xs={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},ks=De.length;function Cs(t,e,n){const{style:s,vars:i,transformOrigin:r}=t;let a=!1,o=!1;for(const t in e){const n=e[t];if(Ie.has(t))a=!0;else if(X(t))i[t]=n;else{const e=On(n,xn[t]);t.startsWith("origin")?(o=!0,r[t]=e):s[t]=e}}if(e.transform||(a||n?s.transform=function(t,e,n){let s="",i=!0;for(let r=0;r{const{transform:n="none"}=getComputedStyle(t);return Ee(n,e)})(t,e);{const s=(n=t,window.getComputedStyle(n)),i=(X(e)?s.getPropertyValue(e):s[e])||0;return"string"==typeof i?i.trim():i}var n}measureInstanceViewportBox(t,{transformPagePoint:e}){return function(t,e){return function({top:t,left:e,right:n,bottom:s}){return{x:{min:e,max:n},y:{min:t,max:s}}}(function(t,e){if(!e)return t;const n=e({x:t.left,y:t.top}),s=e({x:t.right,y:t.bottom});return{top:n.y,left:n.x,bottom:s.y,right:s.x}}(t.getBoundingClientRect(),e))}(t,e)}build(t,e,n){Cs(t,e,n.transformTemplate)}scrapeMotionValuesFromProps(t,e,n){return Os(t,e,n)}}class Is extends As{constructor(){super(...arguments),this.type="object"}readValueFromInstance(t,e){if(function(t,e){return t in e}(e,t)){const n=t[e];if("string"==typeof n||"number"==typeof n)return n}}getBaseTargetFromProps(){}removeValueFromRenderState(t,e){delete e.output[t]}measureInstanceViewportBox(){return{x:{min:0,max:0},y:{min:0,max:0}}}build(t,e){Object.assign(t.output,e)}renderInstance(t,{output:e}){Object.assign(t,e)}sortInstanceNodePosition(){return 0}}const Rs={offset:"stroke-dashoffset",array:"stroke-dasharray"},Bs={offset:"strokeDashoffset",array:"strokeDasharray"};function Ns(t,{attrX:e,attrY:n,attrScale:s,pathLength:i,pathSpacing:r=1,pathOffset:a=0,...o},l,u,h){if(Cs(t,o,u),l)return void(t.style.viewBox&&(t.attrs.viewBox=t.style.viewBox));t.attrs=t.style,t.style={};const{attrs:c,style:p}=t;c.transform&&(p.transform=c.transform,delete c.transform),(p.transform||c.transformOrigin)&&(p.transformOrigin=c.transformOrigin??"50% 50%",delete c.transformOrigin),p.transform&&(p.transformBox=h?.transformBox??"fill-box",delete c.transformBox),void 0!==e&&(c.x=e),void 0!==n&&(c.y=n),void 0!==s&&(c.scale=s),void 0!==i&&function(t,e,n=1,s=0,i=!0){t.pathLength=1;const r=i?Rs:Bs;t[r.offset]=ut.transform(-s);const a=ut.transform(e),o=ut.transform(n);t[r.array]=`${a} ${o}`}(c,i,r,a,!1)}const Ks=new Set(["baseFrequency","diffuseConstant","kernelMatrix","kernelUnitLength","keySplines","keyTimes","limitingConeAngle","markerHeight","markerWidth","numOctaves","targetX","targetY","surfaceScale","specularConstant","specularExponent","stdDeviation","tableValues","viewBox","gradientTransform","pathLength","startOffset","textLength","lengthAdjust"]);class js extends Ss{constructor(){super(...arguments),this.type="svg",this.isSVGTag=!1,this.measureInstanceViewportBox=gs}getBaseTargetFromProps(t,e){return t[e]}readValueFromInstance(t,e){if(Ie.has(e)){const t=Cn(e);return t&&t.default||0}return e=Ks.has(e)?e:is(e),t.getAttribute(e)}scrapeMotionValuesFromProps(t,e,n){return function(t,e,n){const s=Os(t,e,n);for(const n in t)(Bn(t[n])||Bn(e[n]))&&(s[-1!==De.indexOf(n)?"attr"+n.charAt(0).toUpperCase()+n.substring(1):n]=t[n]);return s}(t,e,n)}build(t,e,n){Ns(t,e,this.isSVGTag,n.transformTemplate,n.style)}renderInstance(t,e,n,s){!function(t,e,n,s){Fs(t,e,void 0,s);for(const n in e.attrs)t.setAttribute(Ks.has(n)?n:is(n),e.attrs[n])}(t,e,0,s)}mount(t){var e;this.isSVGTag="string"==typeof(e=t.tagName)&&"svg"===e.toLowerCase(),super.mount(t)}}function Ws(t){const e={presenceContext:null,props:{},visualState:{renderState:{transform:{},transformOrigin:{},style:{},vars:{},attrs:{}},latestValues:{}}},n=Rn(t)&&!function(t){return Rn(t)&&"svg"===t.tagName}(t)?new js(e):new Ds(e);n.mount(t),Gn.set(t,n)}function Ys(t){const e=new Is({presenceContext:null,props:{},visualState:{renderState:{output:{}},latestValues:{}}});e.mount(t),Gn.set(t,e)}function $s(t,e,n,s){const i=[];if(function(t,e){return Bn(t)||"number"==typeof t||"string"==typeof t&&!Kn(e)}(t,e))i.push(function(t,e,n){const s=Bn(t)?t:In(t);return s.start(ps("",s,e,n)),s.animation}(t,Kn(e)&&e.default||e,n&&n.default||n));else{const r=jn(t,e,s),a=r.length;for(let t=0;t{const l=zn(t),{delay:u=0,times:h=me(l),type:c="keyframes",repeat:f,repeatType:y,repeatDelay:v=0,...b}=n;let{ease:w=e.ease||"easeOut",duration:T}=n;const M="function"==typeof u?u(a,o):u,V=l.length,A=tn(c)?c:i?.[c||"keyframes"];if(V<=2&&A){let t=100;if(2===V&&_n(l)){const e=l[1]-l[0];t=Math.abs(e)}const e={...b};void 0!==T&&(e.duration=p(T));const n=$t(e,t,A);w=n.ease,T=n.duration}T??(T=r);const S=d+M;1===h.length&&0===h[0]&&(h[1]=1);const x=h.length-l.length;if(x>0&&de(h,x),1===l.length&&l.unshift(null),f){T=Wn(T,f);const t=[...l],e=[...h];w=Array.isArray(w)?[...w]:[w];const n=[...w];for(let s=0;s{for(const i in t){const r=t[i];r.sort(Un);const o=[],l=[],u=[];for(let t=0;t{s.push(...$s(n,t,e))}),s}function Us(e){return function(n,s,i){let r=[];var a;a=n,r=Array.isArray(a)&&a.some(Array.isArray)?Ls(n,s,e):$s(n,s,i,e);const o=new pn(r);return e&&(e.animations.push(o),o.finished.then(()=>{t(e.animations,o)})),o}}const Xs=Us();export{Xs as animate,Us as createScopedAnimate}; diff --git a/node_modules/framer-motion/dist/size-rollup-dom-animation-assets.js b/node_modules/framer-motion/dist/size-rollup-dom-animation-assets.js new file mode 100644 index 00000000..1b566689 --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-dom-animation-assets.js @@ -0,0 +1 @@ +const t=(t,e,r)=>r>e?e:rs(t[e]))}function i(t){return Boolean(n(t)||t.variants)}const c="undefined"!=typeof window,l={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},f={};for(const t in l)f[t]={isEnabled:e=>l[t].some(t=>!!e[t])};const d=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],p={value:null,addProjectionMetrics:null};function u(t,r){let s=!1,a=!0;const o={delta:0,timestamp:0,isProcessing:!1},n=()=>s=!0,i=d.reduce((t,e)=>(t[e]=function(t,e){let r=new Set,s=new Set,a=!1,o=!1;const n=new WeakSet;let i={delta:0,timestamp:0,isProcessing:!1},c=0;function l(e){n.has(e)&&(f.schedule(e),t()),c++,e(i)}const f={schedule:(t,e=!1,o=!1)=>{const i=o&&a?r:s;return e&&n.add(t),i.has(t)||i.add(t),t},cancel:t=>{s.delete(t),n.delete(t)},process:t=>{i=t,a?o=!0:(a=!0,[r,s]=[s,r],r.forEach(l),e&&p.value&&p.value.frameloop[e].push(c),c=0,r.clear(),a=!1,o&&(o=!1,f.process(t)))}};return f}(n,r?e:void 0),t),{}),{setup:c,read:l,resolveKeyframes:f,preUpdate:u,update:m,preRender:g,render:h,postRender:y}=i,v=()=>{const n=e.useManualTiming?o.timestamp:performance.now();s=!1,e.useManualTiming||(o.delta=a?1e3/60:Math.max(Math.min(n-o.timestamp,40),1)),o.timestamp=n,o.isProcessing=!0,c.process(o),l.process(o),f.process(o),u.process(o),m.process(o),g.process(o),h.process(o),y.process(o),o.isProcessing=!1,s&&r&&(a=!1,t(v))};return{schedule:d.reduce((e,r)=>{const n=i[r];return e[r]=(e,r=!1,i=!1)=>(s||(s=!0,a=!0,o.isProcessing||t(v)),n.schedule(e,r,i)),e},{}),cancel:t=>{for(let e=0;ee=>"string"==typeof e&&e.startsWith(t),g=m("--"),h=m("var(--"),y=t=>!!h(t)&&v.test(t.split("/*")[0].trim()),v=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,w={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},x={...w,transform:e=>t(0,1,e)},b={...w,default:1},k=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),P=k("deg"),B=k("%"),R=k("px"),T=k("vh"),X=k("vw"),Y=(()=>({...B,parse:t=>B.parse(t)/100,transform:t=>B.transform(100*t)}))(),O=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],S=(()=>new Set(O))(),W={...w,transform:Math.round},$={borderWidth:R,borderTopWidth:R,borderRightWidth:R,borderBottomWidth:R,borderLeftWidth:R,borderRadius:R,radius:R,borderTopLeftRadius:R,borderTopRightRadius:R,borderBottomRightRadius:R,borderBottomLeftRadius:R,width:R,maxWidth:R,height:R,maxHeight:R,top:R,right:R,bottom:R,left:R,padding:R,paddingTop:R,paddingRight:R,paddingBottom:R,paddingLeft:R,margin:R,marginTop:R,marginRight:R,marginBottom:R,marginLeft:R,backgroundPositionX:R,backgroundPositionY:R,...{rotate:P,rotateX:P,rotateY:P,rotateZ:P,scale:b,scaleX:b,scaleY:b,scaleZ:b,skew:P,skewX:P,skewY:P,distance:R,translateX:R,translateY:R,translateZ:R,x:R,y:R,z:R,perspective:R,transformPerspective:R,opacity:x,originX:Y,originY:Y,originZ:R},zIndex:W,fillOpacity:x,strokeOpacity:x,numOctaves:W},L=(t,e)=>e&&"number"==typeof t?e.transform(t):t,Z=t=>Boolean(t&&t.getVelocity),V=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),A="data-"+V("framerAppearId"),E={};function F(t,{layout:e,layoutId:r}){return S.has(t)||t.startsWith("origin")||(e||void 0!==r)&&(!!E[t]||"opacity"===t)}const I={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},M=O.length;function z(t,e,r){const{style:s,vars:a,transformOrigin:o}=t;let n=!1,i=!1;for(const t in e){const r=e[t];if(S.has(t))n=!0;else if(g(t))a[t]=r;else{const e=L(r,$[t]);t.startsWith("origin")?(i=!0,o[t]=e):s[t]=e}}if(e.transform||(n||r?s.transform=function(t,e,r){let s="",a=!0;for(let o=0;o"string"==typeof t&&"svg"===t.toLowerCase(),U=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function K(t){return"string"==typeof t&&!t.includes("-")&&!!(U.indexOf(t)>-1||/[A-Z]/u.test(t))}function q(t){const e=[{},{}];return t?.values.forEach((t,r)=>{e[0][r]=t.get(),e[1][r]=t.getVelocity()}),e}function G(t,e,r,s){if("function"==typeof e){const[a,o]=q(s);e=e(void 0!==r?r:t.custom,a,o)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[a,o]=q(s);e=e(void 0!==r?r:t.custom,a,o)}return e}function J(t,e,r){const{style:s}=t,a={};for(const o in s)(Z(s[o])||e.style&&Z(e.style[o])||F(o,t)||void 0!==r?.getValue(o)?.liveStyle)&&(a[o]=s[o]);return a}function N(t,e,r){const s=J(t,e,r);for(const r in t)if(Z(t[r])||Z(e[r])){s[-1!==O.indexOf(r)?"attr"+r.charAt(0).toUpperCase()+r.substring(1):r]=t[r]}return s}export{T as A,$ as B,S as C,o as D,a as E,g as F,V as G,e as M,s as a,c as b,u as c,Z as d,F as e,f,z as g,D as h,n as i,j,K as k,i as l,r as m,N as n,A as o,w as p,x as q,G as r,J as s,t,B as u,y as v,R as w,O as x,P as y,X as z}; diff --git a/node_modules/framer-motion/dist/size-rollup-dom-animation-m.js b/node_modules/framer-motion/dist/size-rollup-dom-animation-m.js new file mode 100644 index 00000000..68dcfab3 --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-dom-animation-m.js @@ -0,0 +1 @@ +import{jsxs as t,jsx as n}from"react/jsx-runtime";import{createContext as e,useContext as o,useMemo as r,useCallback as a,useLayoutEffect as i,useEffect as s,useRef as u,useInsertionEffect as l,forwardRef as c,Fragment as d,createElement as f}from"react";import{i as m,a as p,f as y,b as g,c as v,P as h,S,o as M,m as b,L as w,d as E,e as j,g as C,h as P,j as V,k as A,r as T,l as L,n as W,p as x,s as I,q as O}from"./size-rollup-dom-max-assets.js";const k=e({strict:!1}),D=e({transformPagePoint:t=>t,isStatic:!1,reducedMotion:"never"}),R=e({});function F(t){const{initial:n,animate:e}=function(t,n){if(m(t)){const{initial:n,animate:e}=t;return{initial:!1===n||p(n)?n:void 0,animate:p(e)?e:void 0}}return!1!==t.inherit?n:{}}(t,o(R));return r(()=>({initial:n,animate:e}),[H(n),H(e)])}function H(t){return Array.isArray(t)?t.join(" "):t}const N=Symbol.for("motionComponentSymbol");function q(t,n,e){return a(o=>{o&&t.onMount&&t.onMount(o),n&&(o?n.mount(o):n.unmount()),e&&("function"==typeof e?e(o):g(e)&&(e.current=o))},[n])}const B=v?i:s;function U(t,n,e,r,a){const{visualElement:i}=o(R),c=o(k),d=o(h),f=o(D).reducedMotion,m=u(null);r=r||c.renderer,!m.current&&r&&(m.current=r(t,{visualState:n,parent:i,props:e,presenceContext:d,blockInitialAnimation:!!d&&!1===d.initial,reducedMotionConfig:f}));const p=m.current,y=o(S);!p||p.projection||!a||"html"!==p.type&&"svg"!==p.type||function(t,n,e,o){const{layoutId:r,layout:a,drag:i,dragConstraints:s,layoutScroll:u,layoutRoot:l,layoutCrossfade:c}=n;t.projection=new e(t.latestValues,n["data-framer-portal-id"]?void 0:$(t.parent)),t.projection.setOptions({layoutId:r,layout:a,alwaysMeasureLayout:Boolean(i)||s&&g(s),visualElement:t,animationType:"string"==typeof a?a:"both",initialPromotionConfig:o,crossfade:c,layoutScroll:u,layoutRoot:l})}(m.current,e,a,y);const v=u(!1);l(()=>{p&&v.current&&p.update(e,d)});const w=e[M],E=u(Boolean(w)&&!window.MotionHandoffIsComplete?.(w)&&window.MotionHasOptimisedAnimation?.(w));return B(()=>{p&&(v.current=!0,window.MotionIsMounted=!0,p.updateFeatures(),b.render(p.render),E.current&&p.animationState&&p.animationState.animateChanges())}),s(()=>{p&&(!E.current&&p.animationState&&p.animationState.animateChanges(),E.current&&(queueMicrotask(()=>{window.MotionHandoffMarkAsComplete?.(w)}),E.current=!1))}),p}function $(t){if(t)return!1!==t.options.allowProjection?t.projection:$(t.parent)}function _({preloadedFeatures:e,createVisualElement:r,useRender:a,useVisualState:i,Component:s}){function u(e,u){let l;const c={...o(D),...e,layoutId:z(e)},{isStatic:d}=c,f=F(e),m=i(e,d);if(!d&&v){o(k).strict;const t=function(t){const{drag:n,layout:e}=y;if(!n&&!e)return{};const o={...n,...e};return{MeasureLayout:n?.isEnabled(t)||e?.isEnabled(t)?o.MeasureLayout:void 0,ProjectionNode:o.ProjectionNode}}(c);l=t.MeasureLayout,f.visualElement=U(s,m,c,r,t.ProjectionNode)}return t(R.Provider,{value:f,children:[l&&f.visualElement?n(l,{visualElement:f.visualElement,...c}):null,a(s,e,q(m,f.visualElement,u),m,d,f.visualElement)]})}e&&function(t){for(const n in t)y[n]={...y[n],...t[n]}}(e),u.displayName=`motion.${"string"==typeof s?s:`create(${s.displayName??s.name??""})`}`;const l=c(u);return l[N]=s,l}function z({layoutId:t}){const n=o(w).id;return n&&void 0!==t?n+"-"+t:t}const X=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function Y(t,n,e){for(const o in n)E(n[o])||j(o,e)||(t[o]=n[o])}function G(t,n){const e={};return Y(e,t.style||{},t),Object.assign(e,function({transformTemplate:t},n){return r(()=>{const e={style:{},transform:{},transformOrigin:{},vars:{}};return C(e,n,t),Object.assign({},e.vars,e.style)},[n])}(t,n)),e}function J(t,n){const e={},o=G(t,n);return t.drag&&!1!==t.dragListener&&(e.draggable=!1,o.userSelect=o.WebkitUserSelect=o.WebkitTouchCallout="none",o.touchAction=!0===t.drag?"none":"pan-"+("x"===t.drag?"y":"x")),void 0===t.tabIndex&&(t.onTap||t.onTapStart||t.whileTap)&&(e.tabIndex=0),e.style=o,e}const K=()=>({style:{},transform:{},transformOrigin:{},vars:{},attrs:{}});function Q(t,n,e,o){const a=r(()=>{const e={style:{},transform:{},transformOrigin:{},vars:{},attrs:{}};return P(e,n,V(o),t.transformTemplate,t.style),{...e.attrs,style:{...e.style}}},[n]);if(t.style){const n={};Y(n,t.style,t),a.style={...n,...a.style}}return a}const Z=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","ignoreStrict","viewport"]);function tt(t){return t.startsWith("while")||t.startsWith("drag")&&"draggable"!==t||t.startsWith("layout")||t.startsWith("onTap")||t.startsWith("onPan")||t.startsWith("onLayout")||Z.has(t)}let nt=t=>!tt(t);try{"function"==typeof(et=require("@emotion/is-prop-valid").default)&&(nt=t=>t.startsWith("on")?!tt(t):et(t))}catch{}var et;function ot(t=!1){return(n,e,o,{latestValues:a},i)=>{const s=(A(n)?Q:J)(e,a,i,n),u=function(t,n,e){const o={};for(const r in t)"values"===r&&"object"==typeof t.values||(nt(r)||!0===e&&tt(r)||!n&&!tt(r)||t.draggable&&r.startsWith("onDrag"))&&(o[r]=t[r]);return o}(e,"string"==typeof n,t),l=n!==d?{...u,...s,ref:o}:{},{children:c}=e,m=r(()=>E(c)?c.get():c,[c]);return f(n,{...l,children:m})}}const rt=t=>(n,e)=>{const r=o(R),a=o(h),i=()=>function({scrapeMotionValuesFromProps:t,createRenderState:n},e,o,r){return{latestValues:at(e,o,r,t),renderState:n()}}(t,n,r,a);return e?i():function(t){const n=u(null);return null===n.current&&(n.current=t()),n.current}(i)};function at(t,n,e,o){const r={},a=o(t,{});for(const t in a)r[t]=T(a[t]);let{initial:i,animate:s}=t;const u=m(t),l=L(t);n&&l&&!u&&!1!==t.inherit&&(void 0===i&&(i=n.initial),void 0===s&&(s=n.animate));let c=!!e&&!1===e.initial;c=c||!1===i;const d=c?s:i;if(d&&"boolean"!=typeof d&&!W(d)){const n=Array.isArray(d)?d:[d];for(let e=0;e/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t);const N=t=>/^0[^.\s]+$/u.test(t);function R(t){let e;return()=>(void 0===e&&(e=t()),e)}const B=t=>t,K=(t,e)=>n=>e(t(n)),L=(...t)=>t.reduce(K),j=(t,e,n)=>{const s=e-t;return 0===s?1:(n-t)/s};class U{constructor(){this.subscriptions=[]}add(t){var e,n;return e=this.subscriptions,n=t,-1===e.indexOf(n)&&e.push(n),()=>function(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}(this.subscriptions,t)}notify(t,e,n){const s=this.subscriptions.length;if(s)if(1===s)this.subscriptions[0](t,e,n);else for(let i=0;i1e3*t,W=t=>t/1e3;function Y(t,e){return e?t*(1e3/e):0}const $=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function z(t,e,n,s){if(t===e&&n===s)return B;const i=e=>function(t,e,n,s,i){let r,o,a=0;do{o=e+(n-e)/2,r=$(o,s,i)-t,r>0?n=o:e=o}while(Math.abs(r)>1e-7&&++a<12);return o}(e,0,1,t,n);return t=>0===t||1===t?t:$(i(t),e,s)}const X=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,_=t=>e=>1-t(1-e),H=z(.33,1.53,.69,.99),G=_(H),Z=X(G),J=t=>(t*=2)<1?.5*G(t):.5*(2-Math.pow(2,-10*(t-1))),Q=t=>1-Math.sin(Math.acos(t)),tt=_(Q),et=X(Q),nt=z(.42,0,1,1),st=z(0,0,.58,1),it=z(.42,0,.58,1),rt=t=>Array.isArray(t)&&"number"==typeof t[0],ot={linear:B,easeIn:nt,easeInOut:it,easeOut:st,circIn:Q,circInOut:et,circOut:tt,backIn:G,backInOut:Z,backOut:H,anticipate:J},at=t=>{if(rt(t)){t.length;const[e,n,s,i]=t;return z(e,n,s,i)}return"string"==typeof t?ot[t]:t},{schedule:ut,cancel:lt,state:ht,steps:ct}=t("undefined"!=typeof requestAnimationFrame?requestAnimationFrame:B,!0);let dt;function pt(){dt=void 0}const mt={now:()=>(void 0===dt&&mt.set(ht.isProcessing||e.useManualTiming?ht.timestamp:performance.now()),dt),set:t=>{dt=t,queueMicrotask(pt)}},ft=t=>Math.round(1e5*t)/1e5,yt=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const vt=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,gt=(t,e)=>n=>Boolean("string"==typeof n&&vt.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),bt=(t,e,n)=>s=>{if("string"!=typeof s)return s;const[i,r,o,a]=s.match(yt);return{[t]:parseFloat(i),[e]:parseFloat(r),[n]:parseFloat(o),alpha:void 0!==a?parseFloat(a):1}},wt={...n,transform:t=>Math.round((t=>i(0,255,t))(t))},Tt={test:gt("rgb","red"),parse:bt("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:i=1})=>"rgba("+wt.transform(t)+", "+wt.transform(e)+", "+wt.transform(n)+", "+ft(s.transform(i))+")"};const At={test:gt("#"),parse:function(t){let e="",n="",s="",i="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),s=t.substring(5,7),i=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),s=t.substring(3,4),i=t.substring(4,5),e+=e,n+=n,s+=s,i+=i),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(s,16),alpha:i?parseInt(i,16)/255:1}},transform:Tt.transform},St={test:gt("hsl","hue"),parse:bt("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:i=1})=>"hsla("+Math.round(t)+", "+r.transform(ft(e))+", "+r.transform(ft(n))+", "+ft(s.transform(i))+")"},Vt={test:t=>Tt.test(t)||At.test(t)||St.test(t),parse:t=>Tt.test(t)?Tt.parse(t):St.test(t)?St.parse(t):At.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?Tt.transform(t):St.transform(t),getAnimatableNone:t=>{const e=Vt.parse(t);return e.alpha=0,Vt.transform(e)}},Mt=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const xt="number",Ct="color",kt=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Et(t){const e=t.toString(),n=[],s={color:[],number:[],var:[]},i=[];let r=0;const o=e.replace(kt,t=>(Vt.test(t)?(s.color.push(r),i.push(Ct),n.push(Vt.parse(t))):t.startsWith("var(")?(s.var.push(r),i.push("var"),n.push(t)):(s.number.push(r),i.push(xt),n.push(parseFloat(t))),++r,"${}")).split("${}");return{values:n,split:o,indexes:s,types:i}}function Pt(t){return Et(t).values}function Ft(t){const{split:e,types:n}=Et(t),s=e.length;return t=>{let i="";for(let r=0;r"number"==typeof t?0:Vt.test(t)?Vt.getAnimatableNone(t):t;const Dt={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(yt)?.length||0)+(t.match(Mt)?.length||0)>0},parse:Pt,createTransformer:Ft,getAnimatableNone:function(t){const e=Pt(t);return Ft(t)(e.map(Ot))}};function It(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function Nt(t,e){return n=>n>0?e:t}const Rt=(t,e,n)=>t+(e-t)*n,Bt=(t,e,n)=>{const s=t*t,i=n*(e*e-s)+s;return i<0?0:Math.sqrt(i)},Kt=[At,Tt,St];function Lt(t){const e=(n=t,Kt.find(t=>t.test(n)));var n;if(!Boolean(e))return!1;let s=e.parse(t);return e===St&&(s=function({hue:t,saturation:e,lightness:n,alpha:s}){t/=360,n/=100;let i=0,r=0,o=0;if(e/=100){const s=n<.5?n*(1+e):n+e-n*e,a=2*n-s;i=It(a,s,t+1/3),r=It(a,s,t),o=It(a,s,t-1/3)}else i=r=o=n;return{red:Math.round(255*i),green:Math.round(255*r),blue:Math.round(255*o),alpha:s}}(s)),s}const jt=(t,e)=>{const n=Lt(t),s=Lt(e);if(!n||!s)return Nt(t,e);const i={...n};return t=>(i.red=Bt(n.red,s.red,t),i.green=Bt(n.green,s.green,t),i.blue=Bt(n.blue,s.blue,t),i.alpha=Rt(n.alpha,s.alpha,t),Tt.transform(i))},Ut=new Set(["none","hidden"]);function qt(t,e){return n=>Rt(t,e,n)}function Wt(t){return"number"==typeof t?qt:"string"==typeof t?o(t)?Nt:Vt.test(t)?jt:zt:Array.isArray(t)?Yt:"object"==typeof t?Vt.test(t)?jt:$t:Nt}function Yt(t,e){const n=[...t],s=n.length,i=t.map((t,n)=>Wt(t)(t,e[n]));return t=>{for(let e=0;e{for(const e in s)n[e]=s[e](t);return n}}const zt=(t,e)=>{const n=Dt.createTransformer(e),s=Et(t),i=Et(e);return s.indexes.var.length===i.indexes.var.length&&s.indexes.color.length===i.indexes.color.length&&s.indexes.number.length>=i.indexes.number.length?Ut.has(t)&&!i.values.length||Ut.has(e)&&!s.values.length?function(t,e){return Ut.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}(t,e):L(Yt(function(t,e){const n=[],s={color:0,var:0,number:0};for(let i=0;i{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>ut.update(e,t),stop:()=>lt(e),now:()=>ht.isProcessing?ht.timestamp:mt.now()}},Ht=(t,e,n=10)=>{let s="";const i=Math.max(Math.round(e/n),2);for(let e=0;e=Gt?1/0:e}function Jt(t,e,n){const s=Math.max(e-5,0);return Y(n-t(s),e-s)}const Qt=100,te=10,ee=1,ne=0,se=800,ie=.3,re=.3,oe={granular:.01,default:2},ae={granular:.005,default:.5},ue=.01,le=10,he=.05,ce=1,de=.001;function pe({duration:t=se,bounce:e=ie,velocity:n=ne,mass:s=ee}){let r,o,a=1-e;a=i(he,ce,a),t=i(ue,le,W(t)),a<1?(r=e=>{const s=e*a,i=s*t,r=s-n,o=fe(e,a),u=Math.exp(-i);return de-r/o*u},o=e=>{const s=e*a*t,i=s*n+n,o=Math.pow(a,2)*Math.pow(e,2)*t,u=Math.exp(-s),l=fe(Math.pow(e,2),a);return(-r(e)+de>0?-1:1)*((i-o)*u)/l}):(r=e=>Math.exp(-e*t)*((e-n)*t+1)-.001,o=e=>Math.exp(-e*t)*(t*t*(n-e)));const u=function(t,e,n){let s=n;for(let n=1;nvoid 0!==t[e])}function be(t=re,e=ie){const n="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:s,restDelta:r}=n;const o=n.keyframes[0],a=n.keyframes[n.keyframes.length-1],u={done:!1,value:o},{stiffness:l,damping:h,mass:c,duration:d,velocity:p,isResolvedFromDuration:m}=function(t){let e={velocity:ne,stiffness:Qt,damping:te,mass:ee,isResolvedFromDuration:!1,...t};if(!ge(t,ve)&&ge(t,ye))if(t.visualDuration){const n=t.visualDuration,s=2*Math.PI/(1.2*n),r=s*s,o=2*i(.05,1,1-(t.bounce||0))*Math.sqrt(r);e={...e,mass:ee,stiffness:r,damping:o}}else{const n=pe(t);e={...e,...n,mass:ee},e.isResolvedFromDuration=!0}return e}({...n,velocity:-W(n.velocity||0)}),f=p||0,y=h/(2*Math.sqrt(l*c)),v=a-o,g=W(Math.sqrt(l/c)),b=Math.abs(v)<5;let w;if(s||(s=b?oe.granular:oe.default),r||(r=b?ae.granular:ae.default),y<1){const t=fe(g,y);w=e=>{const n=Math.exp(-y*g*e);return a-n*((f+y*g*v)/t*Math.sin(t*e)+v*Math.cos(t*e))}}else if(1===y)w=t=>a-Math.exp(-g*t)*(v+(f+g*v)*t);else{const t=g*Math.sqrt(y*y-1);w=e=>{const n=Math.exp(-y*g*e),s=Math.min(t*e,300);return a-n*((f+y*g*v)*Math.sinh(s)+t*v*Math.cosh(s))/t}}const T={calculatedDuration:m&&d||null,next:t=>{const e=w(t);if(m)u.done=t>=d;else{let n=0===t?f:0;y<1&&(n=0===t?q(f):Jt(w,t,e));const i=Math.abs(n)<=s,o=Math.abs(a-e)<=r;u.done=i&&o}return u.value=u.done?a:e,u},toString:()=>{const t=Math.min(Zt(T),Gt),e=Ht(e=>T.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return T}function we({keyframes:t,velocity:e=0,power:n=.8,timeConstant:s=325,bounceDamping:i=10,bounceStiffness:r=500,modifyTarget:o,min:a,max:u,restDelta:l=.5,restSpeed:h}){const c=t[0],d={done:!1,value:c},p=t=>void 0===a?u:void 0===u||Math.abs(a-t)-m*Math.exp(-t/s),g=t=>y+v(t),b=t=>{const e=v(t),n=g(t);d.done=Math.abs(e)<=l,d.value=d.done?y:n};let w,T;const A=t=>{var e;(e=d.value,void 0!==a&&eu)&&(w=t,T=be({keyframes:[d.value,p(d.value)],velocity:Jt(g,t,d.value),damping:i,stiffness:r,restDelta:l,restSpeed:h}))};return A(0),{calculatedDuration:null,next:t=>{let e=!1;return T||void 0!==w||(e=!0,b(t),A(t)),void 0!==w&&t>=w?T.next(t-w):(!e&&b(t),d)}}}function Te(t,n,{clamp:s=!0,ease:r,mixer:o}={}){const a=t.length;if(n.length,1===a)return()=>n[0];if(2===a&&n[0]===n[1])return()=>n[1];const u=t[0]===t[1];t[0]>t[a-1]&&(t=[...t].reverse(),n=[...n].reverse());const l=function(t,n,s){const i=[],r=s||e.mix||Xt,o=t.length-1;for(let e=0;e{if(u&&e1)for(;sc(i(t[0],t[a-1],e)):c}function Ae(t){const e=[0];return function(t,e){const n=t[t.length-1];for(let s=1;s<=e;s++){const i=j(0,e,s);t.push(Rt(n,1,i))}}(e,t.length-1),e}function Se({duration:t=300,keyframes:e,times:n,ease:s="easeInOut"}){const i=(t=>Array.isArray(t)&&"number"!=typeof t[0])(s)?s.map(at):at(s),r={done:!1,value:e[0]},o=function(t,e){return t.map(t=>t*e)}(n&&n.length===e.length?n:Ae(e),t),a=Te(o,e,{ease:Array.isArray(i)?i:(u=e,l=i,u.map(()=>l||it).splice(0,u.length-1))});var u,l;return{calculatedDuration:t,next:e=>(r.value=a(e),r.done=e>=t,r)}}be.applyToOptions=t=>{const e=function(t,e=100,n){const s=n({...t,keyframes:[0,e]}),i=Math.min(Zt(s),Gt);return{type:"keyframes",ease:t=>s.next(i*t).value/e,duration:W(i)}}(t,100,be);return t.ease=e.ease,t.duration=q(e.duration),t.type="keyframes",t};const Ve=t=>null!==t;function Me(t,{repeat:e,repeatType:n="loop"},s,i=1){const r=t.filter(Ve),o=i<0||e&&"loop"!==n&&e%2==1?0:r.length-1;return o&&void 0!==s?s:r[o]}const xe={decay:we,inertia:we,tween:Se,keyframes:Se,spring:be};function Ce(t){"string"==typeof t.type&&(t.type=xe[t.type])}class ke{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Ee=t=>t/100;class Pe extends ke{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==mt.now()&&this.tick(mt.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;Ce(t);const{type:e=Se,repeat:n=0,repeatDelay:s=0,repeatType:i,velocity:r=0}=t;let{keyframes:o}=t;const a=e||Se;a!==Se&&"number"!=typeof o[0]&&(this.mixKeyframes=L(Ee,Xt(o[0],o[1])),o=[0,100]);const u=a({...t,keyframes:o});"mirror"===i&&(this.mirroredGenerator=a({...t,keyframes:[...o].reverse(),velocity:-r})),null===u.calculatedDuration&&(u.calculatedDuration=Zt(u));const{calculatedDuration:l}=u;this.calculatedDuration=l,this.resolvedDuration=l+s,this.totalDuration=this.resolvedDuration*(n+1)-s,this.generator=u}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,e=!1){const{generator:n,totalDuration:s,mixKeyframes:r,mirroredGenerator:o,resolvedDuration:a,calculatedDuration:u}=this;if(null===this.startTime)return n.next(0);const{delay:l=0,keyframes:h,repeat:c,repeatType:d,repeatDelay:p,type:m,onUpdate:f,finalKeyframe:y}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-s/this.speed,this.startTime)),e?this.currentTime=t:this.updateTime(t);const v=this.currentTime-l*(this.playbackSpeed>=0?1:-1),g=this.playbackSpeed>=0?v<0:v>s;this.currentTime=Math.max(v,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=s);let b=this.currentTime,w=n;if(c){const t=Math.min(this.currentTime,s)/a;let e=Math.floor(t),n=t%1;!n&&t>=1&&(n=1),1===n&&e--,e=Math.min(e,c+1);Boolean(e%2)&&("reverse"===d?(n=1-n,p&&(n-=p/a)):"mirror"===d&&(w=o)),b=i(0,1,n)*a}const T=g?{done:!1,value:h[0]}:w.next(b);r&&(T.value=r(T.value));let{done:A}=T;g||null===u||(A=this.playbackSpeed>=0?this.currentTime>=s:this.currentTime<=0);const S=null===this.holdTime&&("finished"===this.state||"running"===this.state&&A);return S&&m!==we&&(T.value=Me(h,this.options,y,this.speed)),f&&f(T.value),S&&this.finish(),T}then(t,e){return this.finished.then(t,e)}get duration(){return W(this.calculatedDuration)}get time(){return W(this.currentTime)}set time(t){t=q(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(mt.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=W(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=_t,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const n=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=n):null!==this.holdTime?this.startTime=n-this.holdTime:this.startTime||(this.startTime=e??n),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(mt.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}const Fe=t=>180*t/Math.PI,Oe=t=>{const e=Fe(Math.atan2(t[1],t[0]));return Ie(e)},De={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:Oe,rotateZ:Oe,skewX:t=>Fe(Math.atan(t[1])),skewY:t=>Fe(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},Ie=t=>((t%=360)<0&&(t+=360),t),Ne=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),Re=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),Be={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:Ne,scaleY:Re,scale:t=>(Ne(t)+Re(t))/2,rotateX:t=>Ie(Fe(Math.atan2(t[6],t[5]))),rotateY:t=>Ie(Fe(Math.atan2(-t[2],t[0]))),rotateZ:Oe,rotate:Oe,skewX:t=>Fe(Math.atan(t[4])),skewY:t=>Fe(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function Ke(t){return t.includes("scale")?1:0}function Le(t,e){if(!t||"none"===t)return Ke(e);const n=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let s,i;if(n)s=Be,i=n;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);s=De,i=e}if(!i)return Ke(e);const r=s[e],o=i[1].split(",").map(je);return"function"==typeof r?r(o):o[r]}function je(t){return parseFloat(t.trim())}const Ue=t=>t===n||t===a,qe=new Set(["x","y","z"]),We=u.filter(t=>!qe.has(t));const Ye={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>Le(e,"x"),y:(t,{transform:e})=>Le(e,"y")};Ye.translateX=Ye.x,Ye.translateY=Ye.y;const $e=new Set;let ze=!1,Xe=!1,_e=!1;function He(){if(Xe){const t=Array.from($e).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return We.forEach(n=>{const s=t.getValue(n);void 0!==s&&(e.push([n,s.get()]),s.set(n.startsWith("scale")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{t.getValue(e)?.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}Xe=!1,ze=!1,$e.forEach(t=>t.complete(_e)),$e.clear()}function Ge(){$e.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(Xe=!0)})}class Ze{constructor(t,e,n,s,i,r=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=s,this.element=i,this.isAsync=r}scheduleResolve(){this.state="scheduled",this.isAsync?($e.add(this),ze||(ze=!0,ut.read(Ge),ut.resolveKeyframes(He))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:s}=this;if(null===t[0]){const i=s?.get(),r=t[t.length-1];if(void 0!==i)t[0]=i;else if(n&&e){const s=n.readValue(e,r);null!=s&&(t[0]=s)}void 0===t[0]&&(t[0]=r),s&&void 0===i&&s.set(t[0])}!function(t){for(let e=1;evoid 0!==window.ScrollTimeline),Qe={};function tn(t,e){const n=R(t);return()=>Qe[e]??n()}const en=tn(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),nn=([t,e,n,s])=>`cubic-bezier(${t}, ${e}, ${n}, ${s})`,sn={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:nn([0,.65,.55,1]),circOut:nn([.55,0,1,.45]),backIn:nn([.31,.01,.66,-.59]),backOut:nn([.33,1.53,.69,.99])};function rn(t,e){return t?"function"==typeof t?en()?Ht(t,e):"ease-out":rt(t)?nn(t):Array.isArray(t)?t.map(t=>rn(t,e)||sn.easeOut):sn[t]:void 0}function on(t,e,n,{delay:s=0,duration:i=300,repeat:r=0,repeatType:o="loop",ease:a="easeOut",times:u}={},l=void 0){const h={[e]:n};u&&(h.offset=u);const c=rn(a,i);Array.isArray(c)&&(h.easing=c);const d={delay:s,duration:i,easing:Array.isArray(c)?"linear":c,fill:"both",iterations:r+1,direction:"reverse"===o?"alternate":"normal"};l&&(d.pseudoElement=l);return t.animate(h,d)}function an(t){return"function"==typeof t&&"applyToOptions"in t}class un extends ke{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:n,keyframes:s,pseudoElement:i,allowFlatten:r=!1,finalKeyframe:o,onComplete:a}=t;this.isPseudoElement=Boolean(i),this.allowFlatten=r,this.options=t,t.type;const u=function({type:t,...e}){return an(t)&&en()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=on(e,n,s,u,i),!1===u.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!i){const t=Me(s,this.options,o,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,n){(t=>t.startsWith("--"))(e)?t.style.setProperty(e,n):t.style[e]=n}(e,n,t),this.animation.cancel()}a?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return W(Number(t))}get time(){return W(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=q(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&Je()?(this.animation.timeline=t,B):e(this)}}const ln={anticipate:J,backInOut:Z,circInOut:et};function hn(t){"string"==typeof t.ease&&t.ease in ln&&(t.ease=ln[t.ease])}class cn extends un{constructor(t){hn(t),Ce(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:n,onComplete:s,element:i,...r}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const o=new Pe({...r,autoplay:!1}),a=q(this.finishedTime??this.time);e.setWithVelocity(o.sample(a-10).value,o.sample(a).value,10),o.stop()}}const dn=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!Dt.test(t)&&"0"!==t||t.startsWith("url(")));function pn(t){return"object"==typeof(e=t)&&null!==e&&"offsetHeight"in t;var e}const mn=new Set(["opacity","clipPath","filter","transform"]),fn=R(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));class yn extends ke{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:s=0,repeatDelay:i=0,repeatType:r="loop",keyframes:o,name:a,motionValue:u,element:l,...h}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=mt.now();const c={autoplay:t,delay:e,type:n,repeat:s,repeatDelay:i,repeatType:r,name:a,motionValue:u,element:l,...h},d=l?.KeyframeResolver||Ze;this.keyframeResolver=new d(o,(t,e,n)=>this.onKeyframesResolved(t,e,c,!n),a,u,l),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,n,s,i){this.keyframeResolver=void 0;const{name:r,type:o,velocity:a,delay:u,isHandoff:l,onUpdate:h}=s;this.resolvedAt=mt.now(),function(t,e,n,s){const i=t[0];if(null===i)return!1;if("display"===e||"visibility"===e)return!0;const r=t[t.length-1],o=dn(i,e),a=dn(r,e);return!(!o||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:n,...s,keyframes:t},d=!l&&function(t){const{motionValue:e,name:n,repeatDelay:s,repeatType:i,damping:r,type:o}=t;if(!pn(e?.owner?.current))return!1;const{onUpdate:a,transformTemplate:u}=e.owner.getProps();return fn()&&n&&mn.has(n)&&("transform"!==n||!u)&&!a&&!s&&"mirror"!==i&&0!==r&&"inertia"!==o}(c)?new cn({...c,element:c.motionValue.owner.current}):new Pe(c);d.finished.then(()=>this.notifyFinished()).catch(B),this.pendingTimeline&&(this.stopTimeline=d.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=d}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),_e=!0,Ge(),He(),_e=!1),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}}const vn=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function gn(t,e,n=1){const[s,i]=function(t){const e=vn.exec(t);if(!e)return[,];const[,n,s,i]=e;return[`--${n??s}`,i]}(t);if(!s)return;const r=window.getComputedStyle(e).getPropertyValue(s);if(r){const t=r.trim();return I(t)?parseFloat(t):t}return o(i)?gn(i,e,n+1):i}function bn(t,e){return t?.[e]??t?.default??t}const wn=new Set(["width","height","top","left","right","bottom",...u]),Tn=t=>e=>e.test(t),An=[n,a,r,l,h,c,{test:t=>"auto"===t,parse:t=>t}],Sn=t=>An.find(Tn(t));function Vn(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||N(t))}const Mn=new Set(["brightness","contrast","saturate","opacity"]);function xn(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[s]=n.match(yt)||[];if(!s)return t;const i=n.replace(s,"");let r=Mn.has(e)?1:0;return s!==n&&(r*=100),e+"("+r+i+")"}const Cn=/\b([a-z-]*)\(.*?\)/gu,kn={...Dt,getAnimatableNone:t=>{const e=t.match(Cn);return e?e.map(xn).join(" "):t}},En={...d,color:Vt,backgroundColor:Vt,outlineColor:Vt,fill:Vt,stroke:Vt,borderColor:Vt,borderTopColor:Vt,borderRightColor:Vt,borderBottomColor:Vt,borderLeftColor:Vt,filter:kn,WebkitFilter:kn},Pn=t=>En[t];function Fn(t,e){let n=Pn(t);return n!==kn&&(n=Dt),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const On=new Set(["auto","none","0"]);class Dn extends Ze{constructor(t,e,n,s,i){super(t,e,n,s,i,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{t.getValue(e).set(n)}),this.resolveNoneKeyframes()}}class In{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const n=mt.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=mt.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new U);const n=this.events[t].add(e);return"change"===t?()=>{n(),ut.read(()=>{this.events.change.getSize()||this.stop()})}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=mt.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return Y(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function Nn(t,e){return new In(t,e)}const Rn=!1;function Bn(){return Rn}function Kn(t,e){const n=function(t,e,n){if(t instanceof EventTarget)return[t];if("string"==typeof t){let s=document;e&&(s=e.current);const i=n?.[t]??s.querySelectorAll(t);return i?Array.from(i):[]}return Array.from(t)}(t),s=new AbortController;return[n,{passive:!0,...e,signal:s.signal},()=>s.abort()]}function Ln(t){return!("touch"===t.pointerType||Bn())}const jn=(t,e)=>!!e&&(t===e||jn(t,e.parentElement)),Un=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);const qn=new WeakSet;function Wn(t){return e=>{"Enter"===e.key&&t(e)}}function Yn(t,e){t.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}function $n(t){return(t=>"mouse"===t.pointerType?"number"!=typeof t.button||t.button<=0:!1!==t.isPrimary)(t)&&!Bn()}function zn(t,e,n={}){const[s,i,r]=Kn(t,n),o=t=>{const s=t.currentTarget;if(!$n(t))return;qn.add(s);const r=e(s,t),o=(t,e)=>{window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",u),qn.has(s)&&qn.delete(s),$n(t)&&"function"==typeof r&&r(t,{success:e})},a=t=>{o(t,s===window||s===document||n.useGlobalTarget||jn(s,t.target))},u=t=>{o(t,!1)};window.addEventListener("pointerup",a,i),window.addEventListener("pointercancel",u,i)};return s.forEach(t=>{var e;(n.useGlobalTarget?window:t).addEventListener("pointerdown",o,i),pn(t)&&(t.addEventListener("focus",t=>((t,e)=>{const n=t.currentTarget;if(!n)return;const s=Wn(()=>{if(qn.has(n))return;Yn(n,"down");const t=Wn(()=>{Yn(n,"up")});n.addEventListener("keyup",t,e),n.addEventListener("blur",()=>Yn(n,"cancel"),e)});n.addEventListener("keydown",s,e),n.addEventListener("blur",()=>n.removeEventListener("keydown",s),e)})(t,i)),e=t,Un.has(e.tagName)||-1!==e.tabIndex||t.hasAttribute("tabindex")||(t.tabIndex=0))}),r}const Xn=[...An,Vt,Dt];function _n(t,e,n){const s=t.getProps();return p(s,e,void 0!==n?n:s.custom,t)}const Hn=t=>Array.isArray(t);function Gn(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,Nn(n))}function Zn(t){return Hn(t)?t[t.length-1]||0:t}function Jn(t,n){const s=t.getValue("willChange");if(i=s,Boolean(m(i)&&i.add))return s.add(n);if(!s&&e.WillChange){const s=new e.WillChange("auto");t.addValue("willChange",s),s.add(n)}var i}function Qn(t){return t.props[f]}const ts=t=>null!==t;const es={type:"spring",stiffness:500,damping:25,restSpeed:10},ns={type:"keyframes",duration:.8},ss={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},is=(t,{keyframes:e})=>e.length>2?ns:y.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:es:ss;const rs=(t,n,s,i={},r,o)=>a=>{const u=bn(i,t)||{},l=u.delay||i.delay||0;let{elapsed:h=0}=i;h-=q(l);const c={keyframes:Array.isArray(s)?s:[null,s],ease:"easeOut",velocity:n.getVelocity(),...u,delay:-h,onUpdate:t=>{n.set(t),u.onUpdate&&u.onUpdate(t)},onComplete:()=>{a(),u.onComplete&&u.onComplete()},name:t,motionValue:n,element:o?void 0:r};(function({when:t,delay:e,delayChildren:n,staggerChildren:s,staggerDirection:i,repeat:r,repeatType:o,repeatDelay:a,from:u,elapsed:l,...h}){return!!Object.keys(h).length})(u)||Object.assign(c,is(t,c)),c.duration&&(c.duration=q(c.duration)),c.repeatDelay&&(c.repeatDelay=q(c.repeatDelay)),void 0!==c.from&&(c.keyframes[0]=c.from);let d=!1;if((!1===c.type||0===c.duration&&!c.repeatDelay)&&(c.duration=0,0===c.delay&&(d=!0)),(e.instantAnimations||e.skipAnimations)&&(d=!0,c.duration=0,c.delay=0),c.allowFlatten=!u.type&&!u.ease,d&&!o&&void 0!==n.get()){const t=function(t,{repeat:e,repeatType:n="loop"},s){const i=t.filter(ts),r=e&&"loop"!==n&&e%2==1?0:i.length-1;return r&&void 0!==s?s:i[r]}(c.keyframes,u);if(void 0!==t)return void ut.update(()=>{c.onUpdate(t),c.onComplete()})}return u.isSync?new Pe(c):new yn(c)};function os({protectedKeys:t,needsAnimating:e},n){const s=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,s}function as(t,e,{delay:n=0,transitionOverride:s,type:i}={}){let{transition:r=t.getDefaultTransition(),transitionEnd:o,...a}=e;s&&(r=s);const u=[],l=i&&t.animationState&&t.animationState.getState()[i];for(const e in a){const s=t.getValue(e,t.latestValues[e]??null),i=a[e];if(void 0===i||l&&os(l,e))continue;const o={delay:n,...bn(r||{},e)},h=s.get();if(void 0!==h&&!s.isAnimating&&!Array.isArray(i)&&i===h&&!o.velocity)continue;let c=!1;if(window.MotionHandoffAnimation){const n=Qn(t);if(n){const t=window.MotionHandoffAnimation(n,e,ut);null!==t&&(o.startTime=t,c=!0)}}Jn(t,e),s.start(rs(e,s,i,t.shouldReduceMotion&&wn.has(e)?{type:!1}:o,t,c));const d=s.animation;d&&u.push(d)}return o&&Promise.all(u).then(()=>{ut.update(()=>{o&&function(t,e){const n=_n(t,e);let{transitionEnd:s={},transition:i={},...r}=n||{};r={...r,...s};for(const e in r)Gn(t,e,Zn(r[e]))}(t,o)})}),u}function us(t,e,n={}){const s=_n(t,e,"exit"===n.type?t.presenceContext?.custom:void 0);let{transition:i=t.getDefaultTransition()||{}}=s||{};n.transitionOverride&&(i=n.transitionOverride);const r=s?()=>Promise.all(as(t,s,n)):()=>Promise.resolve(),o=t.variantChildren&&t.variantChildren.size?(s=0)=>{const{delayChildren:r=0,staggerChildren:o,staggerDirection:a}=i;return function(t,e,n=0,s=0,i=0,r=1,o){const a=[],u=t.variantChildren.size,l=(u-1)*i,h="function"==typeof s,c=h?t=>s(t,u):1===r?(t=0)=>t*i:(t=0)=>l-t*i;return Array.from(t.variantChildren).sort(ls).forEach((t,i)=>{t.notify("AnimationStart",e),a.push(us(t,e,{...o,delay:n+(h?0:s)+c(i)}).then(()=>t.notify("AnimationComplete",e)))}),Promise.all(a)}(t,e,s,r,o,a,n)}:()=>Promise.resolve(),{when:a}=i;if(a){const[t,e]="beforeChildren"===a?[r,o]:[o,r];return t().then(()=>e())}return Promise.all([r(),o(n.delay)])}function ls(t,e){return t.sortNodePosition(e)}function hs(t,e){if(!Array.isArray(e))return!1;const n=e.length;if(n!==t.length)return!1;for(let s=0;sPromise.all(e.map(({animation:e,options:n})=>function(t,e,n={}){let s;if(t.notify("AnimationStart",e),Array.isArray(e)){const i=e.map(e=>us(t,e,n));s=Promise.all(i)}else if("string"==typeof e)s=us(t,e,n);else{const i="function"==typeof e?_n(t,e,n.custom):e;s=Promise.all(as(t,i,n))}return s.then(()=>{t.notify("AnimationComplete",e)})}(t,e,n)))}function ys(t){let e=fs(t),n=bs(),s=!0;const i=e=>(n,s)=>{const i=_n(t,s,"exit"===e?t.presenceContext?.custom:void 0);if(i){const{transition:t,transitionEnd:e,...s}=i;n={...n,...s,...e}}return n};function r(r){const{props:o}=t,a=ds(t.parent)||{},u=[],l=new Set;let h={},c=1/0;for(let e=0;ec&&f,A=!1;const S=Array.isArray(m)?m:[m];let V=S.reduce(i(d),{});!1===y&&(V={});const{prevResolvedValues:M={}}=p,x={...M,...V},C=e=>{T=!0,l.has(e)&&(A=!0,l.delete(e)),p.needsAnimating[e]=!0;const n=t.getValue(e);n&&(n.liveStyle=!1)};for(const t in x){const e=V[t],n=M[t];if(h.hasOwnProperty(t))continue;let s=!1;s=Hn(e)&&Hn(n)?!hs(e,n):e!==n,s?null!=e?C(t):l.add(t):void 0!==e&&l.has(t)?C(t):p.protectedKeys[t]=!0}p.prevProp=m,p.prevResolvedValues=V,p.isActive&&(h={...h,...V}),s&&t.blockInitialAnimation&&(T=!1);T&&(!(g&&w)||A)&&u.push(...S.map(t=>({animation:t,options:{type:d}})))}if(l.size){const e={};if("boolean"!=typeof o.initial){const n=_n(t,Array.isArray(o.initial)?o.initial[0]:o.initial);n&&n.transition&&(e.transition=n.transition)}l.forEach(n=>{const s=t.getBaseTarget(n),i=t.getValue(n);i&&(i.liveStyle=!0),e[n]=s??null}),u.push({animation:e})}let d=Boolean(u.length);return!s||!1!==o.initial&&o.initial!==o.animate||t.manuallyAnimateOnMount||(d=!1),s=!1,d?e(u):Promise.resolve()}return{animateChanges:r,setActive:function(e,s){if(n[e].isActive===s)return Promise.resolve();t.variantChildren?.forEach(t=>t.animationState?.setActive(e,s)),n[e].isActive=s;const i=r(e);for(const t in n)n[t].protectedKeys={};return i},setAnimateFunction:function(n){e=n(t)},getState:()=>n,reset:()=>{n=bs(),s=!0}}}function vs(t,e){return"string"==typeof e?e!==t:!!Array.isArray(e)&&!hs(e,t)}function gs(t=!1){return{isActive:t,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function bs(){return{animate:gs(!0),whileInView:gs(),whileHover:gs(),whileTap:gs(),whileDrag:gs(),whileFocus:gs(),exit:gs()}}class ws{constructor(t){this.isMounted=!1,this.node=t}update(){}}let Ts=0;const As={animation:{Feature:class extends ws{constructor(t){super(t),t.animationState||(t.animationState=ys(t))}updateAnimationControlsSubscription(){const{animate:t}=this.node.getProps();b(t)&&(this.unmountControls=t.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:t}=this.node.getProps(),{animate:e}=this.node.prevProps||{};t!==e&&this.updateAnimationControlsSubscription()}unmount(){this.node.animationState.reset(),this.unmountControls?.()}}},exit:{Feature:class extends ws{constructor(){super(...arguments),this.id=Ts++}update(){if(!this.node.presenceContext)return;const{isPresent:t,onExitComplete:e}=this.node.presenceContext,{isPresent:n}=this.node.prevPresenceContext||{};if(!this.node.animationState||t===n)return;const s=this.node.animationState.setActive("exit",!t);e&&!t&&s.then(()=>{e(this.id)})}mount(){const{register:t,onExitComplete:e}=this.node.presenceContext||{};e&&e(this.id),t&&(this.unmount=t(this.id))}unmount(){}}}};function Ss(t){return{point:{x:t.pageX,y:t.pageY}}}function Vs(t,e,n){const{props:s}=t;t.animationState&&s.whileHover&&t.animationState.setActive("whileHover","Start"===n);const i=s["onHover"+n];i&&ut.postRender(()=>i(e,Ss(e)))}function Ms(t,e,n,s={passive:!0}){return t.addEventListener(e,n,s),()=>t.removeEventListener(e,n)}function xs(t,e,n){const{props:s}=t;if(t.current instanceof HTMLButtonElement&&t.current.disabled)return;t.animationState&&s.whileTap&&t.animationState.setActive("whileTap","Start"===n);const i=s["onTap"+("End"===n?"":n)];i&&ut.postRender(()=>i(e,Ss(e)))}const Cs=new WeakMap,ks=new WeakMap,Es=t=>{const e=Cs.get(t.target);e&&e(t)},Ps=t=>{t.forEach(Es)};function Fs(t,e,n){const s=function({root:t,...e}){const n=t||document;ks.has(n)||ks.set(n,{});const s=ks.get(n),i=JSON.stringify(e);return s[i]||(s[i]=new IntersectionObserver(Ps,{root:t,...e})),s[i]}(e);return Cs.set(t,n),s.observe(t),()=>{Cs.delete(t),s.unobserve(t)}}const Os={some:0,all:1};const Ds={inView:{Feature:class extends ws{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){this.unmount();const{viewport:t={}}=this.node.getProps(),{root:e,margin:n,amount:s="some",once:i}=t,r={root:e?e.current:void 0,rootMargin:n,threshold:"number"==typeof s?s:Os[s]};return Fs(this.node.current,r,t=>{const{isIntersecting:e}=t;if(this.isInView===e)return;if(this.isInView=e,i&&!e&&this.hasEnteredView)return;e&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",e);const{onViewportEnter:n,onViewportLeave:s}=this.node.getProps(),r=e?n:s;r&&r(t)})}mount(){this.startObserver()}update(){if("undefined"==typeof IntersectionObserver)return;const{props:t,prevProps:e}=this.node;["amount","margin","root"].some(function({viewport:t={}},{viewport:e={}}={}){return n=>t[n]!==e[n]}(t,e))&&this.startObserver()}unmount(){}}},tap:{Feature:class extends ws{mount(){const{current:t}=this.node;t&&(this.unmount=zn(t,(t,e)=>(xs(this.node,e,"Start"),(t,{success:e})=>xs(this.node,t,e?"End":"Cancel")),{useGlobalTarget:this.node.props.globalTapTarget}))}unmount(){}}},focus:{Feature:class extends ws{constructor(){super(...arguments),this.isActive=!1}onFocus(){let t=!1;try{t=this.node.current.matches(":focus-visible")}catch(e){t=!0}t&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){this.isActive&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=L(Ms(this.node.current,"focus",()=>this.onFocus()),Ms(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}},hover:{Feature:class extends ws{mount(){const{current:t}=this.node;t&&(this.unmount=function(t,e,n={}){const[s,i,r]=Kn(t,n),o=t=>{if(!Ln(t))return;const{target:n}=t,s=e(n,t);if("function"!=typeof s||!n)return;const r=t=>{Ln(t)&&(s(t),n.removeEventListener("pointerleave",r))};n.addEventListener("pointerleave",r,i)};return s.forEach(t=>{t.addEventListener("pointerenter",o,i)}),r}(t,(t,e)=>(Vs(this.node,e,"Start"),t=>Vs(this.node,t,"End"))))}unmount(){}}}};const Is=()=>({x:{min:0,max:0},y:{min:0,max:0}}),Ns={current:null},Rs={current:!1};const Bs=new WeakMap;const Ks=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class Ls{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:s,blockInitialAnimation:i,visualState:r},o={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=Ze,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=mt.now();this.renderScheduledAtthis.bindToMotionValue(e,t)),Rs.current||function(){if(Rs.current=!0,T)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>Ns.current=t.matches;t.addEventListener("change",e),e()}else Ns.current=!1}(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||Ns.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){this.projection&&this.projection.unmount(),lt(this.notifyUpdate),lt(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=y.has(t);n&&this.onBindTransform&&this.onBindTransform();const s=e.on("change",e=>{this.latestValues[t]=e,this.props.onUpdate&&ut.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0)}),i=e.on("renderRequest",this.scheduleRender);let r;window.MotionCheckAppearSync&&(r=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{s(),i(),r&&r(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in V){const e=V[t];if(!e)continue;const{isEnabled:n,Feature:s}=e;if(!this.features[t]&&s&&n(this.props)&&(this.features[t]=new s(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=Nn(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){let n=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];var s;return null!=n&&("string"==typeof n&&(I(n)||N(n))?n=parseFloat(n):(s=n,!Xn.find(Tn(s))&&Dt.test(e)&&(n=Fn(t,e))),this.setBaseTarget(t,m(n)?n.get():n)),m(n)?n.get():n}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){const{initial:e}=this.props;let n;if("string"==typeof e||"object"==typeof e){const s=p(this.props,e,this.presenceContext?.custom);s&&(n=s[t])}if(e&&void 0!==n)return n;const s=this.getBaseTargetFromProps(this.props,t);return void 0===s||m(s)?void 0!==this.initialValues[t]&&void 0===n?void 0:this.baseTarget[t]:s}on(t,e){return this.events[t]||(this.events[t]=new U),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class js extends Ls{constructor(){super(...arguments),this.KeyframeResolver=Dn}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;m(t)&&(this.childSubscription=t.on("change",t=>{this.current&&(this.current.textContent=`${t}`)}))}}function Us(t,{style:e,vars:n},s,i){const r=t.style;let o;for(o in e)r[o]=e[o];for(o in i?.applyProjectionStyles(r,s),n)r.setProperty(o,n[o])}class qs extends js{constructor(){super(...arguments),this.type="html",this.renderInstance=Us}readValueFromInstance(t,e){if(y.has(e))return this.projection?.isProjecting?Ke(e):((t,e)=>{const{transform:n="none"}=getComputedStyle(t);return Le(n,e)})(t,e);{const s=(n=t,window.getComputedStyle(n)),i=(M(e)?s.getPropertyValue(e):s[e])||0;return"string"==typeof i?i.trim():i}var n}measureInstanceViewportBox(t,{transformPagePoint:e}){return function(t,e){return function({top:t,left:e,right:n,bottom:s}){return{x:{min:e,max:n},y:{min:t,max:s}}}(function(t,e){if(!e)return t;const n=e({x:t.left,y:t.top}),s=e({x:t.right,y:t.bottom});return{top:n.y,left:n.x,bottom:s.y,right:s.x}}(t.getBoundingClientRect(),e))}(t,e)}build(t,e,n){x(t,e,n.transformTemplate)}scrapeMotionValuesFromProps(t,e,n){return C(t,e,n)}}const Ws=new Set(["baseFrequency","diffuseConstant","kernelMatrix","kernelUnitLength","keySplines","keyTimes","limitingConeAngle","markerHeight","markerWidth","numOctaves","targetX","targetY","surfaceScale","specularConstant","specularExponent","stdDeviation","tableValues","viewBox","gradientTransform","pathLength","startOffset","textLength","lengthAdjust"]);class Ys extends js{constructor(){super(...arguments),this.type="svg",this.isSVGTag=!1,this.measureInstanceViewportBox=Is}getBaseTargetFromProps(t,e){return t[e]}readValueFromInstance(t,e){if(y.has(e)){const t=Pn(e);return t&&t.default||0}return e=Ws.has(e)?e:k(e),t.getAttribute(e)}scrapeMotionValuesFromProps(t,e,n){return E(t,e,n)}build(t,e,n){P(t,e,this.isSVGTag,n.transformTemplate,n.style)}renderInstance(t,e,n,s){!function(t,e,n,s){Us(t,e,void 0,s);for(const n in e.attrs)t.setAttribute(Ws.has(n)?n:k(n),e.attrs[n])}(t,e,0,s)}mount(t){this.isSVGTag=F(t.tagName),super.mount(t)}}const $s={renderer:(t,e)=>O(t)?new Ys(e):new qs(e,{allowProjection:t!==D}),...As,...Ds};export{$s as domAnimation}; diff --git a/node_modules/framer-motion/dist/size-rollup-dom-max-assets.js b/node_modules/framer-motion/dist/size-rollup-dom-max-assets.js new file mode 100644 index 00000000..00f80ecb --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-dom-max-assets.js @@ -0,0 +1 @@ +import{createContext as t}from"react";const e=(t,e,r)=>r>e?e:ro(t[e]))}function l(t){return Boolean(c(t)||t.variants)}const f="undefined"!=typeof window,p={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},d={};for(const t in p)d[t]={isEnabled:e=>p[t].some(t=>!!e[t])};function u(t){return t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"current")}const m=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],g={value:null,addProjectionMetrics:null};function h(t,e){let s=!1,a=!0;const o={delta:0,timestamp:0,isProcessing:!1},n=()=>s=!0,i=m.reduce((t,r)=>(t[r]=function(t,e){let r=new Set,s=new Set,a=!1,o=!1;const n=new WeakSet;let i={delta:0,timestamp:0,isProcessing:!1},c=0;function l(e){n.has(e)&&(f.schedule(e),t()),c++,e(i)}const f={schedule:(t,e=!1,o=!1)=>{const i=o&&a?r:s;return e&&n.add(t),i.has(t)||i.add(t),t},cancel:t=>{s.delete(t),n.delete(t)},process:t=>{i=t,a?o=!0:(a=!0,[r,s]=[s,r],r.forEach(l),e&&g.value&&g.value.frameloop[e].push(c),c=0,r.clear(),a=!1,o&&(o=!1,f.process(t)))}};return f}(n,e?r:void 0),t),{}),{setup:c,read:l,resolveKeyframes:f,preUpdate:p,update:d,preRender:u,render:h,postRender:y}=i,v=()=>{const n=r.useManualTiming?o.timestamp:performance.now();s=!1,r.useManualTiming||(o.delta=a?1e3/60:Math.max(Math.min(n-o.timestamp,40),1)),o.timestamp=n,o.isProcessing=!0,c.process(o),l.process(o),f.process(o),p.process(o),d.process(o),u.process(o),h.process(o),y.process(o),o.isProcessing=!1,s&&e&&(a=!1,t(v))};return{schedule:m.reduce((e,r)=>{const n=i[r];return e[r]=(e,r=!1,i=!1)=>(s||(s=!0,a=!0,o.isProcessing||t(v)),n.schedule(e,r,i)),e},{}),cancel:t=>{for(let e=0;ee=>"string"==typeof e&&e.startsWith(t),v=y("--"),w=y("var(--"),b=t=>!!w(t)&&x.test(t.split("/*")[0].trim()),x=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,k={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},P={...k,transform:t=>e(0,1,t)},S={...k,default:1},B=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),O=B("deg"),R=B("%"),T=B("px"),X=B("vh"),Y=B("vw"),W=(()=>({...R,parse:t=>R.parse(t)/100,transform:t=>R.transform(100*t)}))(),$=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],L=(()=>new Set($))(),V={...k,transform:Math.round},Z={borderWidth:T,borderTopWidth:T,borderRightWidth:T,borderBottomWidth:T,borderLeftWidth:T,borderRadius:T,radius:T,borderTopLeftRadius:T,borderTopRightRadius:T,borderBottomRightRadius:T,borderBottomLeftRadius:T,width:T,maxWidth:T,height:T,maxHeight:T,top:T,right:T,bottom:T,left:T,padding:T,paddingTop:T,paddingRight:T,paddingBottom:T,paddingLeft:T,margin:T,marginTop:T,marginRight:T,marginBottom:T,marginLeft:T,backgroundPositionX:T,backgroundPositionY:T,...{rotate:O,rotateX:O,rotateY:O,rotateZ:O,scale:S,scaleX:S,scaleY:S,scaleZ:S,skew:O,skewX:O,skewY:O,distance:T,translateX:T,translateY:T,translateZ:T,x:T,y:T,z:T,perspective:T,transformPerspective:T,opacity:P,originX:W,originY:W,originZ:T},zIndex:V,fillOpacity:P,strokeOpacity:P,numOctaves:V},I=(t,e)=>e&&"number"==typeof t?e.transform(t):t,{schedule:M,cancel:A}=h(queueMicrotask,!1),C=t=>Boolean(t&&t.getVelocity),E=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),F="data-"+E("framerAppearId"),H=t(null),z=t({}),j={};function D(t){for(const e in t)j[e]=t[e],v(e)&&(j[e].isCSSVariable=!0)}function K(t,{layout:e,layoutId:r}){return L.has(t)||t.startsWith("origin")||(e||void 0!==r)&&(!!j[t]||"opacity"===t)}const U={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},q=$.length;function G(t,e,r){const{style:s,vars:a,transformOrigin:o}=t;let n=!1,i=!1;for(const t in e){const r=e[t];if(L.has(t))n=!0;else if(v(t))a[t]=r;else{const e=I(r,Z[t]);t.startsWith("origin")?(i=!0,o[t]=e):s[t]=e}}if(e.transform||(n||r?s.transform=function(t,e,r){let s="",a=!0;for(let o=0;o"string"==typeof t&&"svg"===t.toLowerCase(),tt=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function et(t){return"string"==typeof t&&!t.includes("-")&&!!(tt.indexOf(t)>-1||/[A-Z]/u.test(t))}function rt(t){const e=[{},{}];return t?.values.forEach((t,r)=>{e[0][r]=t.get(),e[1][r]=t.getVelocity()}),e}function st(t,e,r,s){if("function"==typeof e){const[a,o]=rt(s);e=e(void 0!==r?r:t.custom,a,o)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[a,o]=rt(s);e=e(void 0!==r?r:t.custom,a,o)}return e}function at(t){return C(t)?t.get():t}function ot(t,e,r){const{style:s}=t,a={};for(const o in s)(C(s[o])||e.style&&C(e.style[o])||K(o,t)||void 0!==r?.getValue(o)?.liveStyle)&&(a[o]=s[o]);return a}function nt(t,e,r){const s=ot(t,e,r);for(const r in t)if(C(t[r])||C(e[r])){s[-1!==$.indexOf(r)?"attr"+r.charAt(0).toUpperCase()+r.substring(1):r]=t[r]}return s}export{$ as A,O as B,Y as C,X as D,Z as E,L as F,j as G,v as H,D as I,i as J,n as K,s as L,r as M,E as N,H as P,z as S,o as a,u as b,f as c,C as d,K as e,d as f,G as g,Q as h,c as i,_ as j,et as k,l,M as m,a as n,F as o,st as p,nt as q,at as r,ot as s,h as t,k as u,P as v,e as w,R as x,b as y,T as z}; diff --git a/node_modules/framer-motion/dist/size-rollup-dom-max.js b/node_modules/framer-motion/dist/size-rollup-dom-max.js new file mode 100644 index 00000000..7afed16d --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-dom-max.js @@ -0,0 +1 @@ +import{t,M as e,u as i,v as s,w as n,x as o,y as r,z as a,A as h,B as l,C as u,D as c,E as d,F as p,d as m,b as f,o as y,m as g,r as v,G as x,c as T,i as P,l as w,f as S,p as b,H as A,g as E,s as V,P as M,L as D,S as C,I as k,a as R,J as L,n as j,K as B,N as F,q as I,h as O,j as U,k as N}from"./size-rollup-dom-max-assets.js";import{jsx as K}from"react/jsx-runtime";import{useContext as W,useId as $,useEffect as z,useCallback as Y,Component as X,Fragment as H}from"react";function G(t,e){-1===t.indexOf(e)&&t.push(e)}function q(t,e){const i=t.indexOf(e);i>-1&&t.splice(i,1)}const _=t=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t);function Z(t){return"object"==typeof t&&null!==t}const J=t=>/^0[^.\s]+$/u.test(t);function Q(t){let e;return()=>(void 0===e&&(e=t()),e)}const tt=t=>t,et=(t,e)=>i=>e(t(i)),it=(...t)=>t.reduce(et),st=(t,e,i)=>{const s=e-t;return 0===s?1:(i-t)/s};class nt{constructor(){this.subscriptions=[]}add(t){return G(this.subscriptions,t),()=>q(this.subscriptions,t)}notify(t,e,i){const s=this.subscriptions.length;if(s)if(1===s)this.subscriptions[0](t,e,i);else for(let n=0;n1e3*t,rt=t=>t/1e3;function at(t,e){return e?t*(1e3/e):0}const ht=(t,e,i)=>(((1-3*i+3*e)*t+(3*i-6*e))*t+3*e)*t;function lt(t,e,i,s){if(t===e&&i===s)return tt;const n=e=>function(t,e,i,s,n){let o,r,a=0;do{r=e+(i-e)/2,o=ht(r,s,n)-t,o>0?i=r:e=r}while(Math.abs(o)>1e-7&&++a<12);return r}(e,0,1,t,i);return t=>0===t||1===t?t:ht(n(t),e,s)}const ut=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,ct=t=>e=>1-t(1-e),dt=lt(.33,1.53,.69,.99),pt=ct(dt),mt=ut(pt),ft=t=>(t*=2)<1?.5*pt(t):.5*(2-Math.pow(2,-10*(t-1))),yt=t=>1-Math.sin(Math.acos(t)),gt=ct(yt),vt=ut(yt),xt=lt(.42,0,1,1),Tt=lt(0,0,.58,1),Pt=lt(.42,0,.58,1),wt=t=>Array.isArray(t)&&"number"==typeof t[0],St={linear:tt,easeIn:xt,easeInOut:Pt,easeOut:Tt,circIn:yt,circInOut:vt,circOut:gt,backIn:pt,backInOut:mt,backOut:dt,anticipate:ft},bt=t=>{if(wt(t)){t.length;const[e,i,s,n]=t;return lt(e,i,s,n)}return"string"==typeof t?St[t]:t},{schedule:At,cancel:Et,state:Vt,steps:Mt}=t("undefined"!=typeof requestAnimationFrame?requestAnimationFrame:tt,!0);let Dt;function Ct(){Dt=void 0}const kt={now:()=>(void 0===Dt&&kt.set(Vt.isProcessing||e.useManualTiming?Vt.timestamp:performance.now()),Dt),set:t=>{Dt=t,queueMicrotask(Ct)}},Rt=t=>Math.round(1e5*t)/1e5,Lt=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const jt=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,Bt=(t,e)=>i=>Boolean("string"==typeof i&&jt.test(i)&&i.startsWith(t)||e&&!function(t){return null==t}(i)&&Object.prototype.hasOwnProperty.call(i,e)),Ft=(t,e,i)=>s=>{if("string"!=typeof s)return s;const[n,o,r,a]=s.match(Lt);return{[t]:parseFloat(n),[e]:parseFloat(o),[i]:parseFloat(r),alpha:void 0!==a?parseFloat(a):1}},It={...i,transform:t=>Math.round((t=>n(0,255,t))(t))},Ot={test:Bt("rgb","red"),parse:Ft("red","green","blue"),transform:({red:t,green:e,blue:i,alpha:n=1})=>"rgba("+It.transform(t)+", "+It.transform(e)+", "+It.transform(i)+", "+Rt(s.transform(n))+")"};const Ut={test:Bt("#"),parse:function(t){let e="",i="",s="",n="";return t.length>5?(e=t.substring(1,3),i=t.substring(3,5),s=t.substring(5,7),n=t.substring(7,9)):(e=t.substring(1,2),i=t.substring(2,3),s=t.substring(3,4),n=t.substring(4,5),e+=e,i+=i,s+=s,n+=n),{red:parseInt(e,16),green:parseInt(i,16),blue:parseInt(s,16),alpha:n?parseInt(n,16)/255:1}},transform:Ot.transform},Nt={test:Bt("hsl","hue"),parse:Ft("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:i,alpha:n=1})=>"hsla("+Math.round(t)+", "+o.transform(Rt(e))+", "+o.transform(Rt(i))+", "+Rt(s.transform(n))+")"},Kt={test:t=>Ot.test(t)||Ut.test(t)||Nt.test(t),parse:t=>Ot.test(t)?Ot.parse(t):Nt.test(t)?Nt.parse(t):Ut.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?Ot.transform(t):Nt.transform(t),getAnimatableNone:t=>{const e=Kt.parse(t);return e.alpha=0,Kt.transform(e)}},Wt=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const $t="number",zt="color",Yt=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Xt(t){const e=t.toString(),i=[],s={color:[],number:[],var:[]},n=[];let o=0;const r=e.replace(Yt,t=>(Kt.test(t)?(s.color.push(o),n.push(zt),i.push(Kt.parse(t))):t.startsWith("var(")?(s.var.push(o),n.push("var"),i.push(t)):(s.number.push(o),n.push($t),i.push(parseFloat(t))),++o,"${}")).split("${}");return{values:i,split:r,indexes:s,types:n}}function Ht(t){return Xt(t).values}function Gt(t){const{split:e,types:i}=Xt(t),s=e.length;return t=>{let n="";for(let o=0;o"number"==typeof t?0:Kt.test(t)?Kt.getAnimatableNone(t):t;const _t={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(Lt)?.length||0)+(t.match(Wt)?.length||0)>0},parse:Ht,createTransformer:Gt,getAnimatableNone:function(t){const e=Ht(t);return Gt(t)(e.map(qt))}};function Zt(t,e,i){return i<0&&(i+=1),i>1&&(i-=1),i<1/6?t+6*(e-t)*i:i<.5?e:i<2/3?t+(e-t)*(2/3-i)*6:t}function Jt(t,e){return i=>i>0?e:t}const Qt=(t,e,i)=>t+(e-t)*i,te=(t,e,i)=>{const s=t*t,n=i*(e*e-s)+s;return n<0?0:Math.sqrt(n)},ee=[Ut,Ot,Nt];function ie(t){const e=(i=t,ee.find(t=>t.test(i)));var i;if(!Boolean(e))return!1;let s=e.parse(t);return e===Nt&&(s=function({hue:t,saturation:e,lightness:i,alpha:s}){t/=360,i/=100;let n=0,o=0,r=0;if(e/=100){const s=i<.5?i*(1+e):i+e-i*e,a=2*i-s;n=Zt(a,s,t+1/3),o=Zt(a,s,t),r=Zt(a,s,t-1/3)}else n=o=r=i;return{red:Math.round(255*n),green:Math.round(255*o),blue:Math.round(255*r),alpha:s}}(s)),s}const se=(t,e)=>{const i=ie(t),s=ie(e);if(!i||!s)return Jt(t,e);const n={...i};return t=>(n.red=te(i.red,s.red,t),n.green=te(i.green,s.green,t),n.blue=te(i.blue,s.blue,t),n.alpha=Qt(i.alpha,s.alpha,t),Ot.transform(n))},ne=new Set(["none","hidden"]);function oe(t,e){return i=>Qt(t,e,i)}function re(t){return"number"==typeof t?oe:"string"==typeof t?r(t)?Jt:Kt.test(t)?se:le:Array.isArray(t)?ae:"object"==typeof t?Kt.test(t)?se:he:Jt}function ae(t,e){const i=[...t],s=i.length,n=t.map((t,i)=>re(t)(t,e[i]));return t=>{for(let e=0;e{for(const e in s)i[e]=s[e](t);return i}}const le=(t,e)=>{const i=_t.createTransformer(e),s=Xt(t),n=Xt(e);return s.indexes.var.length===n.indexes.var.length&&s.indexes.color.length===n.indexes.color.length&&s.indexes.number.length>=n.indexes.number.length?ne.has(t)&&!n.values.length||ne.has(e)&&!s.values.length?function(t,e){return ne.has(t)?i=>i<=0?t:e:i=>i>=1?e:t}(t,e):it(ae(function(t,e){const i=[],s={color:0,var:0,number:0};for(let n=0;n{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>At.update(e,t),stop:()=>Et(e),now:()=>Vt.isProcessing?Vt.timestamp:kt.now()}},de=(t,e,i=10)=>{let s="";const n=Math.max(Math.round(e/i),2);for(let e=0;e=pe?1/0:e}function fe(t,e,i){const s=Math.max(e-5,0);return at(i-t(s),e-s)}const ye=100,ge=10,ve=1,xe=0,Te=800,Pe=.3,we=.3,Se={granular:.01,default:2},be={granular:.005,default:.5},Ae=.01,Ee=10,Ve=.05,Me=1,De=.001;function Ce({duration:t=Te,bounce:e=Pe,velocity:i=xe,mass:s=ve}){let o,r,a=1-e;a=n(Ve,Me,a),t=n(Ae,Ee,rt(t)),a<1?(o=e=>{const s=e*a,n=s*t,o=s-i,r=Re(e,a),h=Math.exp(-n);return De-o/r*h},r=e=>{const s=e*a*t,n=s*i+i,r=Math.pow(a,2)*Math.pow(e,2)*t,h=Math.exp(-s),l=Re(Math.pow(e,2),a);return(-o(e)+De>0?-1:1)*((n-r)*h)/l}):(o=e=>Math.exp(-e*t)*((e-i)*t+1)-.001,r=e=>Math.exp(-e*t)*(t*t*(i-e)));const h=function(t,e,i){let s=i;for(let i=1;ivoid 0!==t[e])}function Fe(t=we,e=Pe){const i="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:s,restDelta:o}=i;const r=i.keyframes[0],a=i.keyframes[i.keyframes.length-1],h={done:!1,value:r},{stiffness:l,damping:u,mass:c,duration:d,velocity:p,isResolvedFromDuration:m}=function(t){let e={velocity:xe,stiffness:ye,damping:ge,mass:ve,isResolvedFromDuration:!1,...t};if(!Be(t,je)&&Be(t,Le))if(t.visualDuration){const i=t.visualDuration,s=2*Math.PI/(1.2*i),o=s*s,r=2*n(.05,1,1-(t.bounce||0))*Math.sqrt(o);e={...e,mass:ve,stiffness:o,damping:r}}else{const i=Ce(t);e={...e,...i,mass:ve},e.isResolvedFromDuration=!0}return e}({...i,velocity:-rt(i.velocity||0)}),f=p||0,y=u/(2*Math.sqrt(l*c)),g=a-r,v=rt(Math.sqrt(l/c)),x=Math.abs(g)<5;let T;if(s||(s=x?Se.granular:Se.default),o||(o=x?be.granular:be.default),y<1){const t=Re(v,y);T=e=>{const i=Math.exp(-y*v*e);return a-i*((f+y*v*g)/t*Math.sin(t*e)+g*Math.cos(t*e))}}else if(1===y)T=t=>a-Math.exp(-v*t)*(g+(f+v*g)*t);else{const t=v*Math.sqrt(y*y-1);T=e=>{const i=Math.exp(-y*v*e),s=Math.min(t*e,300);return a-i*((f+y*v*g)*Math.sinh(s)+t*g*Math.cosh(s))/t}}const P={calculatedDuration:m&&d||null,next:t=>{const e=T(t);if(m)h.done=t>=d;else{let i=0===t?f:0;y<1&&(i=0===t?ot(f):fe(T,t,e));const n=Math.abs(i)<=s,r=Math.abs(a-e)<=o;h.done=n&&r}return h.value=h.done?a:e,h},toString:()=>{const t=Math.min(me(P),pe),e=de(e=>P.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return P}function Ie({keyframes:t,velocity:e=0,power:i=.8,timeConstant:s=325,bounceDamping:n=10,bounceStiffness:o=500,modifyTarget:r,min:a,max:h,restDelta:l=.5,restSpeed:u}){const c=t[0],d={done:!1,value:c},p=t=>void 0===a?h:void 0===h||Math.abs(a-t)-m*Math.exp(-t/s),v=t=>y+g(t),x=t=>{const e=g(t),i=v(t);d.done=Math.abs(e)<=l,d.value=d.done?y:i};let T,P;const w=t=>{var e;(e=d.value,void 0!==a&&eh)&&(T=t,P=Fe({keyframes:[d.value,p(d.value)],velocity:fe(v,t,d.value),damping:n,stiffness:o,restDelta:l,restSpeed:u}))};return w(0),{calculatedDuration:null,next:t=>{let e=!1;return P||void 0!==T||(e=!0,x(t),w(t)),void 0!==T&&t>=T?P.next(t-T):(!e&&x(t),d)}}}function Oe(t,i,{clamp:s=!0,ease:o,mixer:r}={}){const a=t.length;if(i.length,1===a)return()=>i[0];if(2===a&&i[0]===i[1])return()=>i[1];const h=t[0]===t[1];t[0]>t[a-1]&&(t=[...t].reverse(),i=[...i].reverse());const l=function(t,i,s){const n=[],o=s||e.mix||ue,r=t.length-1;for(let e=0;e{if(h&&e1)for(;sc(n(t[0],t[a-1],e)):c}function Ue(t){const e=[0];return function(t,e){const i=t[t.length-1];for(let s=1;s<=e;s++){const n=st(0,e,s);t.push(Qt(i,1,n))}}(e,t.length-1),e}function Ne({duration:t=300,keyframes:e,times:i,ease:s="easeInOut"}){const n=(t=>Array.isArray(t)&&"number"!=typeof t[0])(s)?s.map(bt):bt(s),o={done:!1,value:e[0]},r=function(t,e){return t.map(t=>t*e)}(i&&i.length===e.length?i:Ue(e),t),a=Oe(r,e,{ease:Array.isArray(n)?n:(h=e,l=n,h.map(()=>l||Pt).splice(0,h.length-1))});var h,l;return{calculatedDuration:t,next:e=>(o.value=a(e),o.done=e>=t,o)}}Fe.applyToOptions=t=>{const e=function(t,e=100,i){const s=i({...t,keyframes:[0,e]}),n=Math.min(me(s),pe);return{type:"keyframes",ease:t=>s.next(n*t).value/e,duration:rt(n)}}(t,100,Fe);return t.ease=e.ease,t.duration=ot(e.duration),t.type="keyframes",t};const Ke=t=>null!==t;function We(t,{repeat:e,repeatType:i="loop"},s,n=1){const o=t.filter(Ke),r=n<0||e&&"loop"!==i&&e%2==1?0:o.length-1;return r&&void 0!==s?s:o[r]}const $e={decay:Ie,inertia:Ie,tween:Ne,keyframes:Ne,spring:Fe};function ze(t){"string"==typeof t.type&&(t.type=$e[t.type])}class Ye{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Xe=t=>t/100;class He extends Ye{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==kt.now()&&this.tick(kt.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;ze(t);const{type:e=Ne,repeat:i=0,repeatDelay:s=0,repeatType:n,velocity:o=0}=t;let{keyframes:r}=t;const a=e||Ne;a!==Ne&&"number"!=typeof r[0]&&(this.mixKeyframes=it(Xe,ue(r[0],r[1])),r=[0,100]);const h=a({...t,keyframes:r});"mirror"===n&&(this.mirroredGenerator=a({...t,keyframes:[...r].reverse(),velocity:-o})),null===h.calculatedDuration&&(h.calculatedDuration=me(h));const{calculatedDuration:l}=h;this.calculatedDuration=l,this.resolvedDuration=l+s,this.totalDuration=this.resolvedDuration*(i+1)-s,this.generator=h}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,e=!1){const{generator:i,totalDuration:s,mixKeyframes:o,mirroredGenerator:r,resolvedDuration:a,calculatedDuration:h}=this;if(null===this.startTime)return i.next(0);const{delay:l=0,keyframes:u,repeat:c,repeatType:d,repeatDelay:p,type:m,onUpdate:f,finalKeyframe:y}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-s/this.speed,this.startTime)),e?this.currentTime=t:this.updateTime(t);const g=this.currentTime-l*(this.playbackSpeed>=0?1:-1),v=this.playbackSpeed>=0?g<0:g>s;this.currentTime=Math.max(g,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=s);let x=this.currentTime,T=i;if(c){const t=Math.min(this.currentTime,s)/a;let e=Math.floor(t),i=t%1;!i&&t>=1&&(i=1),1===i&&e--,e=Math.min(e,c+1);Boolean(e%2)&&("reverse"===d?(i=1-i,p&&(i-=p/a)):"mirror"===d&&(T=r)),x=n(0,1,i)*a}const P=v?{done:!1,value:u[0]}:T.next(x);o&&(P.value=o(P.value));let{done:w}=P;v||null===h||(w=this.playbackSpeed>=0?this.currentTime>=s:this.currentTime<=0);const S=null===this.holdTime&&("finished"===this.state||"running"===this.state&&w);return S&&m!==Ie&&(P.value=We(u,this.options,y,this.speed)),f&&f(P.value),S&&this.finish(),P}then(t,e){return this.finished.then(t,e)}get duration(){return rt(this.calculatedDuration)}get time(){return rt(this.currentTime)}set time(t){t=ot(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(kt.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=rt(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=ce,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const i=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=i):null!==this.holdTime?this.startTime=i-this.holdTime:this.startTime||(this.startTime=e??i),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(kt.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}const Ge=t=>180*t/Math.PI,qe=t=>{const e=Ge(Math.atan2(t[1],t[0]));return Ze(e)},_e={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:qe,rotateZ:qe,skewX:t=>Ge(Math.atan(t[1])),skewY:t=>Ge(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},Ze=t=>((t%=360)<0&&(t+=360),t),Je=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),Qe=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),ti={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:Je,scaleY:Qe,scale:t=>(Je(t)+Qe(t))/2,rotateX:t=>Ze(Ge(Math.atan2(t[6],t[5]))),rotateY:t=>Ze(Ge(Math.atan2(-t[2],t[0]))),rotateZ:qe,rotate:qe,skewX:t=>Ge(Math.atan(t[4])),skewY:t=>Ge(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function ei(t){return t.includes("scale")?1:0}function ii(t,e){if(!t||"none"===t)return ei(e);const i=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let s,n;if(i)s=ti,n=i;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);s=_e,n=e}if(!n)return ei(e);const o=s[e],r=n[1].split(",").map(si);return"function"==typeof o?o(r):r[o]}function si(t){return parseFloat(t.trim())}const ni=t=>t===i||t===a,oi=new Set(["x","y","z"]),ri=h.filter(t=>!oi.has(t));const ai={width:({x:t},{paddingLeft:e="0",paddingRight:i="0"})=>t.max-t.min-parseFloat(e)-parseFloat(i),height:({y:t},{paddingTop:e="0",paddingBottom:i="0"})=>t.max-t.min-parseFloat(e)-parseFloat(i),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>ii(e,"x"),y:(t,{transform:e})=>ii(e,"y")};ai.translateX=ai.x,ai.translateY=ai.y;const hi=new Set;let li=!1,ui=!1,ci=!1;function di(){if(ui){const t=Array.from(hi).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),i=new Map;e.forEach(t=>{const e=function(t){const e=[];return ri.forEach(i=>{const s=t.getValue(i);void 0!==s&&(e.push([i,s.get()]),s.set(i.startsWith("scale")?1:0))}),e}(t);e.length&&(i.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=i.get(t);e&&e.forEach(([e,i])=>{t.getValue(e)?.set(i)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}ui=!1,li=!1,hi.forEach(t=>t.complete(ci)),hi.clear()}function pi(){hi.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(ui=!0)})}class mi{constructor(t,e,i,s,n,o=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=i,this.motionValue=s,this.element=n,this.isAsync=o}scheduleResolve(){this.state="scheduled",this.isAsync?(hi.add(this),li||(li=!0,At.read(pi),At.resolveKeyframes(di))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:i,motionValue:s}=this;if(null===t[0]){const n=s?.get(),o=t[t.length-1];if(void 0!==n)t[0]=n;else if(i&&e){const s=i.readValue(e,o);null!=s&&(t[0]=s)}void 0===t[0]&&(t[0]=o),s&&void 0===n&&s.set(t[0])}!function(t){for(let e=1;evoid 0!==window.ScrollTimeline),yi={};function gi(t,e){const i=Q(t);return()=>yi[e]??i()}const vi=gi(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),xi=([t,e,i,s])=>`cubic-bezier(${t}, ${e}, ${i}, ${s})`,Ti={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:xi([0,.65,.55,1]),circOut:xi([.55,0,1,.45]),backIn:xi([.31,.01,.66,-.59]),backOut:xi([.33,1.53,.69,.99])};function Pi(t,e){return t?"function"==typeof t?vi()?de(t,e):"ease-out":wt(t)?xi(t):Array.isArray(t)?t.map(t=>Pi(t,e)||Ti.easeOut):Ti[t]:void 0}function wi(t,e,i,{delay:s=0,duration:n=300,repeat:o=0,repeatType:r="loop",ease:a="easeOut",times:h}={},l=void 0){const u={[e]:i};h&&(u.offset=h);const c=Pi(a,n);Array.isArray(c)&&(u.easing=c);const d={delay:s,duration:n,easing:Array.isArray(c)?"linear":c,fill:"both",iterations:o+1,direction:"reverse"===r?"alternate":"normal"};l&&(d.pseudoElement=l);return t.animate(u,d)}function Si(t){return"function"==typeof t&&"applyToOptions"in t}class bi extends Ye{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:i,keyframes:s,pseudoElement:n,allowFlatten:o=!1,finalKeyframe:r,onComplete:a}=t;this.isPseudoElement=Boolean(n),this.allowFlatten=o,this.options=t,t.type;const h=function({type:t,...e}){return Si(t)&&vi()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=wi(e,i,s,h,n),!1===h.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!n){const t=We(s,this.options,r,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,i){(t=>t.startsWith("--"))(e)?t.style.setProperty(e,i):t.style[e]=i}(e,i,t),this.animation.cancel()}a?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return rt(Number(t))}get time(){return rt(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=ot(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&fi()?(this.animation.timeline=t,tt):e(this)}}const Ai={anticipate:ft,backInOut:mt,circInOut:vt};function Ei(t){"string"==typeof t.ease&&t.ease in Ai&&(t.ease=Ai[t.ease])}class Vi extends bi{constructor(t){Ei(t),ze(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:i,onComplete:s,element:n,...o}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const r=new He({...o,autoplay:!1}),a=ot(this.finishedTime??this.time);e.setWithVelocity(r.sample(a-10).value,r.sample(a).value,10),r.stop()}}const Mi=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!_t.test(t)&&"0"!==t||t.startsWith("url(")));function Di(t){return Z(t)&&"offsetHeight"in t}const Ci=new Set(["opacity","clipPath","filter","transform"]),ki=Q(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));class Ri extends Ye{constructor({autoplay:t=!0,delay:e=0,type:i="keyframes",repeat:s=0,repeatDelay:n=0,repeatType:o="loop",keyframes:r,name:a,motionValue:h,element:l,...u}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=kt.now();const c={autoplay:t,delay:e,type:i,repeat:s,repeatDelay:n,repeatType:o,name:a,motionValue:h,element:l,...u},d=l?.KeyframeResolver||mi;this.keyframeResolver=new d(r,(t,e,i)=>this.onKeyframesResolved(t,e,c,!i),a,h,l),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,i,s,n){this.keyframeResolver=void 0;const{name:o,type:r,velocity:a,delay:h,isHandoff:l,onUpdate:u}=s;this.resolvedAt=kt.now(),function(t,e,i,s){const n=t[0];if(null===n)return!1;if("display"===e||"visibility"===e)return!0;const o=t[t.length-1],r=Mi(n,e),a=Mi(o,e);return!(!r||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let i=0;i40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:i,...s,keyframes:t},d=!l&&function(t){const{motionValue:e,name:i,repeatDelay:s,repeatType:n,damping:o,type:r}=t;if(!Di(e?.owner?.current))return!1;const{onUpdate:a,transformTemplate:h}=e.owner.getProps();return ki()&&i&&Ci.has(i)&&("transform"!==i||!h)&&!a&&!s&&"mirror"!==n&&0!==o&&"inertia"!==r}(c)?new Vi({...c,element:c.motionValue.owner.current}):new He(c);d.finished.then(()=>this.notifyFinished()).catch(tt),this.pendingTimeline&&(this.stopTimeline=d.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=d}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),ci=!0,pi(),di(),ci=!1),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}}const Li=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function ji(t,e,i=1){const[s,n]=function(t){const e=Li.exec(t);if(!e)return[,];const[,i,s,n]=e;return[`--${i??s}`,n]}(t);if(!s)return;const o=window.getComputedStyle(e).getPropertyValue(s);if(o){const t=o.trim();return _(t)?parseFloat(t):t}return r(n)?ji(n,e,i+1):n}function Bi(t,e){return t?.[e]??t?.default??t}const Fi=new Set(["width","height","top","left","right","bottom",...h]),Ii=t=>e=>e.test(t),Oi=[i,a,o,l,u,c,{test:t=>"auto"===t,parse:t=>t}],Ui=t=>Oi.find(Ii(t));function Ni(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||J(t))}const Ki=new Set(["brightness","contrast","saturate","opacity"]);function Wi(t){const[e,i]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[s]=i.match(Lt)||[];if(!s)return t;const n=i.replace(s,"");let o=Ki.has(e)?1:0;return s!==i&&(o*=100),e+"("+o+n+")"}const $i=/\b([a-z-]*)\(.*?\)/gu,zi={..._t,getAnimatableNone:t=>{const e=t.match($i);return e?e.map(Wi).join(" "):t}},Yi={...d,color:Kt,backgroundColor:Kt,outlineColor:Kt,fill:Kt,stroke:Kt,borderColor:Kt,borderTopColor:Kt,borderRightColor:Kt,borderBottomColor:Kt,borderLeftColor:Kt,filter:zi,WebkitFilter:zi},Xi=t=>Yi[t];function Hi(t,e){let i=Xi(t);return i!==zi&&(i=_t),i.getAnimatableNone?i.getAnimatableNone(e):void 0}const Gi=new Set(["auto","none","0"]);class qi extends mi{constructor(t,e,i,s,n){super(t,e,i,s,n,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:i}=this;if(!e||!e.current)return;super.readKeyframes();for(let i=0;i{t.getValue(e).set(i)}),this.resolveNoneKeyframes()}}class _i{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const i=kt.now();if(this.updatedAt!==i&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=kt.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new nt);const i=this.events[t].add(e);return"change"===t?()=>{i(),At.read(()=>{this.events.change.getSize()||this.stop()})}:i}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,i){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-i}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=kt.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return at(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function Zi(t,e){return new _i(t,e)}const Ji={x:!1,y:!1};function Qi(){return Ji.x||Ji.y}function ts(t,e){const i=function(t,e,i){if(t instanceof EventTarget)return[t];if("string"==typeof t){let s=document;e&&(s=e.current);const n=i?.[t]??s.querySelectorAll(t);return n?Array.from(n):[]}return Array.from(t)}(t),s=new AbortController;return[i,{passive:!0,...e,signal:s.signal},()=>s.abort()]}function es(t){return!("touch"===t.pointerType||Qi())}const is=(t,e)=>!!e&&(t===e||is(t,e.parentElement)),ss=t=>"mouse"===t.pointerType?"number"!=typeof t.button||t.button<=0:!1!==t.isPrimary,ns=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);const os=new WeakSet;function rs(t){return e=>{"Enter"===e.key&&t(e)}}function as(t,e){t.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}function hs(t){return ss(t)&&!Qi()}function ls(t,e,i={}){const[s,n,o]=ts(t,i),r=t=>{const s=t.currentTarget;if(!hs(t))return;os.add(s);const o=e(s,t),r=(t,e)=>{window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",h),os.has(s)&&os.delete(s),hs(t)&&"function"==typeof o&&o(t,{success:e})},a=t=>{r(t,s===window||s===document||i.useGlobalTarget||is(s,t.target))},h=t=>{r(t,!1)};window.addEventListener("pointerup",a,n),window.addEventListener("pointercancel",h,n)};return s.forEach(t=>{var e;(i.useGlobalTarget?window:t).addEventListener("pointerdown",r,n),Di(t)&&(t.addEventListener("focus",t=>((t,e)=>{const i=t.currentTarget;if(!i)return;const s=rs(()=>{if(os.has(i))return;as(i,"down");const t=rs(()=>{as(i,"up")});i.addEventListener("keyup",t,e),i.addEventListener("blur",()=>as(i,"cancel"),e)});i.addEventListener("keydown",s,e),i.addEventListener("blur",()=>i.removeEventListener("keydown",s),e)})(t,n)),e=t,ns.has(e.tagName)||-1!==e.tabIndex||t.hasAttribute("tabindex")||(t.tabIndex=0))}),o}function us(t){return Z(t)&&"ownerSVGElement"in t}const cs=[...Oi,Kt,_t];class ds{constructor(t){this.isMounted=!1,this.node=t}update(){}}const ps=t=>null!==t;const ms={type:"spring",stiffness:500,damping:25,restSpeed:10},fs={type:"keyframes",duration:.8},ys={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},gs=(t,{keyframes:e})=>e.length>2?fs:p.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:ms:ys;const vs=(t,i,s,n={},o,r)=>a=>{const h=Bi(n,t)||{},l=h.delay||n.delay||0;let{elapsed:u=0}=n;u-=ot(l);const c={keyframes:Array.isArray(s)?s:[null,s],ease:"easeOut",velocity:i.getVelocity(),...h,delay:-u,onUpdate:t=>{i.set(t),h.onUpdate&&h.onUpdate(t)},onComplete:()=>{a(),h.onComplete&&h.onComplete()},name:t,motionValue:i,element:r?void 0:o};(function({when:t,delay:e,delayChildren:i,staggerChildren:s,staggerDirection:n,repeat:o,repeatType:r,repeatDelay:a,from:h,elapsed:l,...u}){return!!Object.keys(u).length})(h)||Object.assign(c,gs(t,c)),c.duration&&(c.duration=ot(c.duration)),c.repeatDelay&&(c.repeatDelay=ot(c.repeatDelay)),void 0!==c.from&&(c.keyframes[0]=c.from);let d=!1;if((!1===c.type||0===c.duration&&!c.repeatDelay)&&(c.duration=0,0===c.delay&&(d=!0)),(e.instantAnimations||e.skipAnimations)&&(d=!0,c.duration=0,c.delay=0),c.allowFlatten=!h.type&&!h.ease,d&&!r&&void 0!==i.get()){const t=function(t,{repeat:e,repeatType:i="loop"},s){const n=t.filter(ps),o=e&&"loop"!==i&&e%2==1?0:n.length-1;return o&&void 0!==s?s:n[o]}(c.keyframes,h);if(void 0!==t)return void At.update(()=>{c.onUpdate(t),c.onComplete()})}return h.isSync?new He(c):new Ri(c)};function xs(t,e,i,s={passive:!0}){return t.addEventListener(e,i,s),()=>t.removeEventListener(e,i)}function Ts(t){return{point:{x:t.pageX,y:t.pageY}}}function Ps(t,e,i,s){return xs(t,e,(t=>e=>ss(e)&&t(e,Ts(e)))(i),s)}function ws({top:t,left:e,right:i,bottom:s}){return{x:{min:e,max:i},y:{min:t,max:s}}}function Ss(t){return t.max-t.min}function bs(t,e,i,s=.5){t.origin=s,t.originPoint=Qt(e.min,e.max,t.origin),t.scale=Ss(i)/Ss(e),t.translate=Qt(i.min,i.max,t.origin)-t.originPoint,(t.scale>=.9999&&t.scale<=1.0001||isNaN(t.scale))&&(t.scale=1),(t.translate>=-.01&&t.translate<=.01||isNaN(t.translate))&&(t.translate=0)}function As(t,e,i,s){bs(t.x,e.x,i.x,s?s.originX:void 0),bs(t.y,e.y,i.y,s?s.originY:void 0)}function Es(t,e,i){t.min=i.min+e.min,t.max=t.min+Ss(e)}function Vs(t,e,i){t.min=e.min-i.min,t.max=t.min+Ss(e)}function Ms(t,e,i){Vs(t.x,e.x,i.x),Vs(t.y,e.y,i.y)}const Ds=()=>({x:{min:0,max:0},y:{min:0,max:0}});function Cs(t){return[t("x"),t("y")]}function ks(t){return void 0===t||1===t}function Rs({scale:t,scaleX:e,scaleY:i}){return!ks(t)||!ks(e)||!ks(i)}function Ls(t){return Rs(t)||js(t)||t.z||t.rotate||t.rotateX||t.rotateY||t.skewX||t.skewY}function js(t){return Bs(t.x)||Bs(t.y)}function Bs(t){return t&&"0%"!==t}function Fs(t,e,i){return i+e*(t-i)}function Is(t,e,i,s,n){return void 0!==n&&(t=Fs(t,n,s)),Fs(t,i,s)+e}function Os(t,e=0,i=1,s,n){t.min=Is(t.min,e,i,s,n),t.max=Is(t.max,e,i,s,n)}function Us(t,{x:e,y:i}){Os(t.x,e.translate,e.scale,e.originPoint),Os(t.y,i.translate,i.scale,i.originPoint)}const Ns=.999999999999,Ks=1.0000000000001;function Ws(t,e){t.min=t.min+e,t.max=t.max+e}function $s(t,e,i,s,n=.5){Os(t,e,i,Qt(t.min,t.max,n),s)}function zs(t,e){$s(t.x,e.x,e.scaleX,e.scale,e.originX),$s(t.y,e.y,e.scaleY,e.scale,e.originY)}function Ys(t,e){return ws(function(t,e){if(!e)return t;const i=e({x:t.left,y:t.top}),s=e({x:t.right,y:t.bottom});return{top:i.y,left:i.x,bottom:s.y,right:s.x}}(t.getBoundingClientRect(),e))}const Xs=({current:t})=>t?t.ownerDocument.defaultView:null;function Hs(t,i){const s=t.getValue("willChange");if(n=s,Boolean(m(n)&&n.add))return s.add(i);if(!s&&e.WillChange){const s=new e.WillChange("auto");t.addValue("willChange",s),s.add(i)}var n}const Gs=(t,e)=>Math.abs(t-e);class qs{constructor(t,e,{transformPagePoint:i,contextWindow:s=window,dragSnapToOrigin:n=!1,distanceThreshold:o=3}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.updatePoint=()=>{if(!this.lastMoveEvent||!this.lastMoveEventInfo)return;const t=Js(this.lastMoveEventInfo,this.history),e=null!==this.startEvent,i=function(t,e){const i=Gs(t.x,e.x),s=Gs(t.y,e.y);return Math.sqrt(i**2+s**2)}(t.offset,{x:0,y:0})>=this.distanceThreshold;if(!e&&!i)return;const{point:s}=t,{timestamp:n}=Vt;this.history.push({...s,timestamp:n});const{onStart:o,onMove:r}=this.handlers;e||(o&&o(this.lastMoveEvent,t),this.startEvent=this.lastMoveEvent),r&&r(this.lastMoveEvent,t)},this.handlePointerMove=(t,e)=>{this.lastMoveEvent=t,this.lastMoveEventInfo=_s(e,this.transformPagePoint),At.update(this.updatePoint,!0)},this.handlePointerUp=(t,e)=>{this.end();const{onEnd:i,onSessionEnd:s,resumeAnimation:n}=this.handlers;if(this.dragSnapToOrigin&&n&&n(),!this.lastMoveEvent||!this.lastMoveEventInfo)return;const o=Js("pointercancel"===t.type?this.lastMoveEventInfo:_s(e,this.transformPagePoint),this.history);this.startEvent&&i&&i(t,o),s&&s(t,o)},!ss(t))return;this.dragSnapToOrigin=n,this.handlers=e,this.transformPagePoint=i,this.distanceThreshold=o,this.contextWindow=s||window;const r=_s(Ts(t),this.transformPagePoint),{point:a}=r,{timestamp:h}=Vt;this.history=[{...a,timestamp:h}];const{onSessionStart:l}=e;l&&l(t,Js(r,this.history)),this.removeListeners=it(Ps(this.contextWindow,"pointermove",this.handlePointerMove),Ps(this.contextWindow,"pointerup",this.handlePointerUp),Ps(this.contextWindow,"pointercancel",this.handlePointerUp))}updateHandlers(t){this.handlers=t}end(){this.removeListeners&&this.removeListeners(),Et(this.updatePoint)}}function _s(t,e){return e?{point:e(t.point)}:t}function Zs(t,e){return{x:t.x-e.x,y:t.y-e.y}}function Js({point:t},e){return{point:t,delta:Zs(t,tn(e)),offset:Zs(t,Qs(e)),velocity:en(e,.1)}}function Qs(t){return t[0]}function tn(t){return t[t.length-1]}function en(t,e){if(t.length<2)return{x:0,y:0};let i=t.length-1,s=null;const n=tn(t);for(;i>=0&&(s=t[i],!(n.timestamp-s.timestamp>ot(e)));)i--;if(!s)return{x:0,y:0};const o=rt(n.timestamp-s.timestamp);if(0===o)return{x:0,y:0};const r={x:(n.x-s.x)/o,y:(n.y-s.y)/o};return r.x===1/0&&(r.x=0),r.y===1/0&&(r.y=0),r}function sn(t,e,i){return{min:void 0!==e?t.min+e:void 0,max:void 0!==i?t.max+i-(t.max-t.min):void 0}}function nn(t,e){let i=e.min-t.min,s=e.max-t.max;return e.max-e.min{const{dragSnapToOrigin:i}=this.getProps();i?this.pauseAnimation():this.stopAnimation(),e&&this.snapToCursor(Ts(t).point)},onStart:(t,e)=>{const{drag:i,dragPropagation:s,onDragStart:n}=this.getProps();if(i&&!s&&(this.openDragLock&&this.openDragLock(),this.openDragLock="x"===(r=i)||"y"===r?Ji[r]?null:(Ji[r]=!0,()=>{Ji[r]=!1}):Ji.x||Ji.y?null:(Ji.x=Ji.y=!0,()=>{Ji.x=Ji.y=!1}),!this.openDragLock))return;var r;this.latestPointerEvent=t,this.latestPanInfo=e,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),Cs(t=>{let e=this.getAxisMotionValue(t).get()||0;if(o.test(e)){const{projection:i}=this.visualElement;if(i&&i.layout){const s=i.layout.layoutBox[t];if(s){e=Ss(s)*(parseFloat(e)/100)}}}this.originPoint[t]=e}),n&&At.postRender(()=>n(t,e)),Hs(this.visualElement,"transform");const{animationState:a}=this.visualElement;a&&a.setActive("whileDrag",!0)},onMove:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e;const{dragPropagation:i,dragDirectionLock:s,onDirectionLock:n,onDrag:o}=this.getProps();if(!i&&!this.openDragLock)return;const{offset:r}=e;if(s&&null===this.currentDirection)return this.currentDirection=function(t,e=10){let i=null;Math.abs(t.y)>e?i="y":Math.abs(t.x)>e&&(i="x");return i}(r),void(null!==this.currentDirection&&n&&n(this.currentDirection));this.updateAxis("x",e.point,r),this.updateAxis("y",e.point,r),this.visualElement.render(),o&&o(t,e)},onSessionEnd:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e,this.stop(t,e),this.latestPointerEvent=null,this.latestPanInfo=null},resumeAnimation:()=>Cs(t=>"paused"===this.getAnimationState(t)&&this.getAxisMotionValue(t).animation?.play())},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:n,distanceThreshold:i,contextWindow:Xs(this.visualElement)})}stop(t,e){const i=t||this.latestPointerEvent,s=e||this.latestPanInfo,n=this.isDragging;if(this.cancel(),!n||!s||!i)return;const{velocity:o}=s;this.startAnimation(o);const{onDragEnd:r}=this.getProps();r&&At.postRender(()=>r(i,s))}cancel(){this.isDragging=!1;const{projection:t,animationState:e}=this.visualElement;t&&(t.isAnimationBlocked=!1),this.panSession&&this.panSession.end(),this.panSession=void 0;const{dragPropagation:i}=this.getProps();!i&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),e&&e.setActive("whileDrag",!1)}updateAxis(t,e,i){const{drag:s}=this.getProps();if(!i||!un(t,s,this.currentDirection))return;const n=this.getAxisMotionValue(t);let o=this.originPoint[t]+i[t];this.constraints&&this.constraints[t]&&(o=function(t,{min:e,max:i},s){return void 0!==e&&ti&&(t=s?Qt(i,t,s.max):Math.min(t,i)),t}(o,this.constraints[t],this.elastic[t])),n.set(o)}resolveConstraints(){const{dragConstraints:t,dragElastic:e}=this.getProps(),i=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):this.visualElement.projection?.layout,s=this.constraints;t&&f(t)?this.constraints||(this.constraints=this.resolveRefConstraints()):this.constraints=!(!t||!i)&&function(t,{top:e,left:i,bottom:s,right:n}){return{x:sn(t.x,i,n),y:sn(t.y,e,s)}}(i.layoutBox,t),this.elastic=function(t=on){return!1===t?t=0:!0===t&&(t=on),{x:rn(t,"left","right"),y:rn(t,"top","bottom")}}(e),s!==this.constraints&&i&&this.constraints&&!this.hasMutatedConstraints&&Cs(t=>{!1!==this.constraints&&this.getAxisMotionValue(t)&&(this.constraints[t]=function(t,e){const i={};return void 0!==e.min&&(i.min=e.min-t.min),void 0!==e.max&&(i.max=e.max-t.min),i}(i.layoutBox[t],this.constraints[t]))})}resolveRefConstraints(){const{dragConstraints:t,onMeasureDragConstraints:e}=this.getProps();if(!t||!f(t))return!1;const i=t.current,{projection:s}=this.visualElement;if(!s||!s.layout)return!1;const n=function(t,e,i){const s=Ys(t,i),{scroll:n}=e;return n&&(Ws(s.x,n.offset.x),Ws(s.y,n.offset.y)),s}(i,s.root,this.visualElement.getTransformPagePoint());let o=function(t,e){return{x:nn(t.x,e.x),y:nn(t.y,e.y)}}(s.layout.layoutBox,n);if(e){const t=e(function({x:t,y:e}){return{top:e.min,right:t.max,bottom:e.max,left:t.min}}(o));this.hasMutatedConstraints=!!t,t&&(o=ws(t))}return o}startAnimation(t){const{drag:e,dragMomentum:i,dragElastic:s,dragTransition:n,dragSnapToOrigin:o,onDragTransitionEnd:r}=this.getProps(),a=this.constraints||{},h=Cs(r=>{if(!un(r,e,this.currentDirection))return;let h=a&&a[r]||{};o&&(h={min:0,max:0});const l=s?200:1e6,u=s?40:1e7,c={type:"inertia",velocity:i?t[r]:0,bounceStiffness:l,bounceDamping:u,timeConstant:750,restDelta:1,restSpeed:10,...n,...h};return this.startAxisValueAnimation(r,c)});return Promise.all(h).then(r)}startAxisValueAnimation(t,e){const i=this.getAxisMotionValue(t);return Hs(this.visualElement,t),i.start(vs(t,i,0,e,this.visualElement,!1))}stopAnimation(){Cs(t=>this.getAxisMotionValue(t).stop())}pauseAnimation(){Cs(t=>this.getAxisMotionValue(t).animation?.pause())}getAnimationState(t){return this.getAxisMotionValue(t).animation?.state}getAxisMotionValue(t){const e=`_drag${t.toUpperCase()}`,i=this.visualElement.getProps(),s=i[e];return s||this.visualElement.getValue(t,(i.initial?i.initial[t]:void 0)||0)}snapToCursor(t){Cs(e=>{const{drag:i}=this.getProps();if(!un(e,i,this.currentDirection))return;const{projection:s}=this.visualElement,n=this.getAxisMotionValue(e);if(s&&s.layout){const{min:i,max:o}=s.layout.layoutBox[e];n.set(t[e]-Qt(i,o,.5))}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:t,dragConstraints:e}=this.getProps(),{projection:i}=this.visualElement;if(!f(e)||!i||!this.constraints)return;this.stopAnimation();const s={x:0,y:0};Cs(t=>{const e=this.getAxisMotionValue(t);if(e&&!1!==this.constraints){const i=e.get();s[t]=function(t,e){let i=.5;const s=Ss(t),o=Ss(e);return o>s?i=st(e.min,e.max-s,t.min):s>o&&(i=st(t.min,t.max-o,e.min)),n(0,1,i)}({min:i,max:i},this.constraints[t])}});const{transformTemplate:o}=this.visualElement.getProps();this.visualElement.current.style.transform=o?o({},""):"none",i.root&&i.root.updateScroll(),i.updateLayout(),this.resolveConstraints(),Cs(e=>{if(!un(e,t,null))return;const i=this.getAxisMotionValue(e),{min:n,max:o}=this.constraints[e];i.set(Qt(n,o,s[e]))})}addListeners(){if(!this.visualElement.current)return;hn.set(this.visualElement,this);const t=Ps(this.visualElement.current,"pointerdown",t=>{const{drag:e,dragListener:i=!0}=this.getProps();e&&i&&this.start(t)}),e=()=>{const{dragConstraints:t}=this.getProps();f(t)&&t.current&&(this.constraints=this.resolveRefConstraints())},{projection:i}=this.visualElement,s=i.addEventListener("measure",e);i&&!i.layout&&(i.root&&i.root.updateScroll(),i.updateLayout()),At.read(e);const n=xs(window,"resize",()=>this.scalePositionWithinConstraints()),o=i.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e})=>{this.isDragging&&e&&(Cs(e=>{const i=this.getAxisMotionValue(e);i&&(this.originPoint[e]+=t[e].translate,i.set(i.get()+t[e].translate))}),this.visualElement.render())});return()=>{n(),t(),s(),o&&o()}}getProps(){const t=this.visualElement.getProps(),{drag:e=!1,dragDirectionLock:i=!1,dragPropagation:s=!1,dragConstraints:n=!1,dragElastic:o=on,dragMomentum:r=!0}=t;return{...t,drag:e,dragDirectionLock:i,dragPropagation:s,dragConstraints:n,dragElastic:o,dragMomentum:r}}}function un(t,e,i){return!(!0!==e&&e!==t||null!==i&&i!==t)}const cn=t=>(e,i)=>{t&&At.postRender(()=>t(e,i))};function dn(t){return t.props[y]}const pn=(t,e)=>t.depth-e.depth;class mn{constructor(){this.children=[],this.isDirty=!1}add(t){G(this.children,t),this.isDirty=!0}remove(t){q(this.children,t),this.isDirty=!0}forEach(t){this.isDirty&&this.children.sort(pn),this.isDirty=!1,this.children.forEach(t)}}const fn=["TopLeft","TopRight","BottomLeft","BottomRight"],yn=fn.length,gn=t=>"string"==typeof t?parseFloat(t):t,vn=t=>"number"==typeof t||a.test(t);function xn(t,e){return void 0!==t[e]?t[e]:t.borderRadius}const Tn=wn(0,.5,gt),Pn=wn(.5,.95,tt);function wn(t,e,i){return s=>se?1:i(st(t,e,s))}function Sn(t,e){t.min=e.min,t.max=e.max}function bn(t,e){Sn(t.x,e.x),Sn(t.y,e.y)}function An(t,e){t.translate=e.translate,t.scale=e.scale,t.originPoint=e.originPoint,t.origin=e.origin}function En(t,e,i,s,n){return t=Fs(t-=e,1/i,s),void 0!==n&&(t=Fs(t,1/n,s)),t}function Vn(t,e,[i,s,n],r,a){!function(t,e=0,i=1,s=.5,n,r=t,a=t){o.test(e)&&(e=parseFloat(e),e=Qt(a.min,a.max,e/100)-a.min);if("number"!=typeof e)return;let h=Qt(r.min,r.max,s);t===r&&(h-=e),t.min=En(t.min,e,i,h,n),t.max=En(t.max,e,i,h,n)}(t,e[i],e[s],e[n],e.scale,r,a)}const Mn=["x","scaleX","originX"],Dn=["y","scaleY","originY"];function Cn(t,e,i,s){Vn(t.x,e,Mn,i?i.x:void 0,s?s.x:void 0),Vn(t.y,e,Dn,i?i.y:void 0,s?s.y:void 0)}function kn(t){return 0===t.translate&&1===t.scale}function Rn(t){return kn(t.x)&&kn(t.y)}function Ln(t,e){return t.min===e.min&&t.max===e.max}function jn(t,e){return Math.round(t.min)===Math.round(e.min)&&Math.round(t.max)===Math.round(e.max)}function Bn(t,e){return jn(t.x,e.x)&&jn(t.y,e.y)}function Fn(t){return Ss(t.x)/Ss(t.y)}function In(t,e){return t.translate===e.translate&&t.scale===e.scale&&t.originPoint===e.originPoint}class On{constructor(){this.members=[]}add(t){G(this.members,t),t.scheduleRender()}remove(t){if(q(this.members,t),t===this.prevLead&&(this.prevLead=void 0),t===this.lead){const t=this.members[this.members.length-1];t&&this.promote(t)}}relegate(t){const e=this.members.findIndex(e=>t===e);if(0===e)return!1;let i;for(let t=e;t>=0;t--){const e=this.members[t];if(!1!==e.isPresent){i=e;break}}return!!i&&(this.promote(i),!0)}promote(t,e){const i=this.lead;if(t!==i&&(this.prevLead=i,this.lead=t,t.show(),i)){i.instance&&i.scheduleRender(),t.scheduleRender(),t.resumeFrom=i,e&&(t.resumeFrom.preserveOpacity=!0),i.snapshot&&(t.snapshot=i.snapshot,t.snapshot.latestValues=i.animationValues||i.latestValues),t.root&&t.root.isUpdating&&(t.isLayoutDirty=!0);const{crossfade:s}=t.options;!1===s&&i.hide()}}exitAnimationComplete(){this.members.forEach(t=>{const{options:e,resumingFrom:i}=t;e.onExitComplete&&e.onExitComplete(),i&&i.options.onExitComplete&&i.options.onExitComplete()})}scheduleRender(){this.members.forEach(t=>{t.instance&&t.scheduleRender(!1)})}removeLeadSnapshot(){this.lead&&this.lead.snapshot&&(this.lead.snapshot=void 0)}}const Un={hasAnimatedSinceResize:!0,hasEverUpdated:!1},Nn=["","X","Y","Z"];let Kn=0;function Wn(t,e,i,s){const{latestValues:n}=e;n[t]&&(i[t]=n[t],e.setStaticValue(t,0),s&&(s[t]=0))}function $n(t){if(t.hasCheckedOptimisedAppear=!0,t.root===t)return;const{visualElement:e}=t.options;if(!e)return;const i=dn(e);if(window.MotionHasOptimisedAnimation(i,"transform")){const{layout:e,layoutId:s}=t.options;window.MotionCancelOptimisedAnimation(i,"transform",At,!(e||s))}const{parent:s}=t;s&&!s.hasCheckedOptimisedAppear&&$n(s)}function zn({attachResizeListener:t,defaultParent:e,measureScroll:i,checkIsScrollRoot:s,resetTransform:r}){return class{constructor(t={},i=e?.()){this.id=Kn++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,this.nodes.forEach(Hn),this.nodes.forEach(to),this.nodes.forEach(eo),this.nodes.forEach(Gn)},this.resolvedRelativeTargetAt=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=t,this.root=i?i.root||i:this,this.path=i?[...i.path,i]:[],this.parent=i,this.depth=i?i.depth+1:0;for(let t=0;tthis.root.updateBlockedByResize=!1;At.read(()=>{s=window.innerWidth}),t(e,()=>{const t=window.innerWidth;t!==s&&(s=t,this.root.updateBlockedByResize=!0,i&&i(),i=function(t,e){const i=kt.now(),s=({timestamp:n})=>{const o=n-i;o>=e&&(Et(s),t(o-e))};return At.setup(s,!0),()=>Et(s)}(n,250),Un.hasAnimatedSinceResize&&(Un.hasAnimatedSinceResize=!1,this.nodes.forEach(Qn)))})}s&&this.root.registerSharedNode(s,this),!1!==this.options.animate&&o&&(s||n)&&this.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e,hasRelativeLayoutChanged:i,layout:s})=>{if(this.isTreeAnimationBlocked())return this.target=void 0,void(this.relativeTarget=void 0);const n=this.options.transition||o.getDefaultTransition()||ao,{onLayoutAnimationStart:r,onLayoutAnimationComplete:a}=o.getProps(),h=!this.targetLayout||!Bn(this.targetLayout,s),l=!e&&i;if(this.options.layoutRoot||this.resumeFrom||l||e&&(h||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const e={...Bi(n,"layout"),onPlay:r,onComplete:a};(o.shouldReduceMotion||this.options.layoutRoot)&&(e.delay=0,e.type=!1),this.startAnimation(e),this.setAnimationOrigin(t,l)}else e||Qn(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=s})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const t=this.getStack();t&&t.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),Et(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(io),this.animationId++)}getTransformTemplate(){const{visualElement:t}=this.options;return t&&t.getProps().transformTemplate}willUpdate(t=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked())return void(this.options.onExitComplete&&this.options.onExitComplete());if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&$n(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let t=0;t{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){!this.snapshot&&this.instance&&(this.snapshot=this.measure(),!this.snapshot||Ss(this.snapshot.measuredBox.x)||Ss(this.snapshot.measuredBox.y)||(this.snapshot=void 0))}updateLayout(){if(!this.instance)return;if(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead()||this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let t=0;tNs&&(e.x=1),e.yNs&&(e.y=1)}(this.layoutCorrected,this.treeScale,this.path,e),!t.layout||t.target||1===this.treeScale.x&&1===this.treeScale.y||(t.target=t.layout.layoutBox,t.targetWithTransforms={x:{min:0,max:0},y:{min:0,max:0}});const{target:a}=t;a?(this.projectionDelta&&this.prevProjectionDelta?(An(this.prevProjectionDelta.x,this.projectionDelta.x),An(this.prevProjectionDelta.y,this.projectionDelta.y)):this.createProjectionDeltas(),As(this.projectionDelta,this.layoutCorrected,a,this.latestValues),this.treeScale.x===o&&this.treeScale.y===r&&In(this.projectionDelta.x,this.prevProjectionDelta.x)&&In(this.projectionDelta.y,this.prevProjectionDelta.y)||(this.hasProjected=!0,this.scheduleRender(),this.notifyListeners("projectionUpdate",a))):this.prevProjectionDelta&&(this.createProjectionDeltas(),this.scheduleRender())}hide(){this.isVisible=!1}show(){this.isVisible=!0}scheduleRender(t=!0){if(this.options.visualElement?.scheduleRender(),t){const t=this.getStack();t&&t.scheduleRender()}this.resumingFrom&&!this.resumingFrom.instance&&(this.resumingFrom=void 0)}createProjectionDeltas(){this.prevProjectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDeltaWithTransform={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}}}setAnimationOrigin(t,e=!1){const i=this.snapshot,s=i?i.latestValues:{},n={...this.latestValues},r={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};this.relativeParent&&this.relativeParent.options.layoutRoot||(this.relativeTarget=this.relativeTargetOrigin=void 0),this.attemptToResolveRelativeTarget=!e;const a={x:{min:0,max:0},y:{min:0,max:0}},h=(i?i.source:void 0)!==(this.layout?this.layout.source:void 0),l=this.getStack(),u=!l||l.members.length<=1,c=Boolean(h&&!u&&!0===this.options.crossfade&&!this.path.some(ro));let d;this.animationProgress=0,this.mixTargetDelta=e=>{const i=e/1e3;var l,p,m,f,y,g;no(r.x,t.x,i),no(r.y,t.y,i),this.setTargetDelta(r),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(Ms(a,this.layout.layoutBox,this.relativeParent.layout.layoutBox),m=this.relativeTarget,f=this.relativeTargetOrigin,y=a,g=i,oo(m.x,f.x,y.x,g),oo(m.y,f.y,y.y,g),d&&(l=this.relativeTarget,p=d,Ln(l.x,p.x)&&Ln(l.y,p.y))&&(this.isProjectionDirty=!1),d||(d={x:{min:0,max:0},y:{min:0,max:0}}),bn(d,this.relativeTarget)),h&&(this.animationValues=n,function(t,e,i,s,n,r){n?(t.opacity=Qt(0,i.opacity??1,Tn(s)),t.opacityExit=Qt(e.opacity??1,0,Pn(s))):r&&(t.opacity=Qt(e.opacity??1,i.opacity??1,s));for(let n=0;n{Un.hasAnimatedSinceResize=!0,this.motionValue||(this.motionValue=Zi(0)),this.currentAnimation=function(t,e,i){const s=m(t)?t:Zi(t);return s.start(vs("",s,e,i)),s.animation}(this.motionValue,[0,1e3],{...t,velocity:0,isSync:!0,onUpdate:e=>{this.mixTargetDelta(e),t.onUpdate&&t.onUpdate(e)},onStop:()=>{},onComplete:()=>{t.onComplete&&t.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const t=this.getStack();t&&t.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners("animationComplete")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(1e3),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const t=this.getLead();let{targetWithTransforms:e,target:i,layout:s,latestValues:n}=t;if(e&&i&&s){if(this!==t&&this.layout&&s&&co(this.options.animationType,this.layout.layoutBox,s.layoutBox)){i=this.target||{x:{min:0,max:0},y:{min:0,max:0}};const e=Ss(this.layout.layoutBox.x);i.x.min=t.target.x.min,i.x.max=i.x.min+e;const s=Ss(this.layout.layoutBox.y);i.y.min=t.target.y.min,i.y.max=i.y.min+s}bn(e,i),zs(e,n),As(this.projectionDeltaWithTransform,this.layoutCorrected,e,n)}}registerSharedNode(t,e){this.sharedNodes.has(t)||this.sharedNodes.set(t,new On);this.sharedNodes.get(t).add(e);const i=e.options.initialPromotionConfig;e.promote({transition:i?i.transition:void 0,preserveFollowOpacity:i&&i.shouldPreserveFollowOpacity?i.shouldPreserveFollowOpacity(e):void 0})}isLead(){const t=this.getStack();return!t||t.lead===this}getLead(){const{layoutId:t}=this.options;return t&&this.getStack()?.lead||this}getPrevLead(){const{layoutId:t}=this.options;return t?this.getStack()?.prevLead:void 0}getStack(){const{layoutId:t}=this.options;if(t)return this.root.sharedNodes.get(t)}promote({needsReset:t,transition:e,preserveFollowOpacity:i}={}){const s=this.getStack();s&&s.promote(this,i),t&&(this.projectionDelta=void 0,this.needsReset=!0),e&&this.setOptions({transition:e})}relegate(){const t=this.getStack();return!!t&&t.relegate(this)}resetSkewAndRotation(){const{visualElement:t}=this.options;if(!t)return;let e=!1;const{latestValues:i}=t;if((i.z||i.rotate||i.rotateX||i.rotateY||i.rotateZ||i.skewX||i.skewY)&&(e=!0),!e)return;const s={};i.z&&Wn("z",t,s,this.animationValues);for(let e=0;et.currentAnimation?.stop()),this.root.nodes.forEach(_n),this.root.sharedNodes.clear()}}}function Yn(t){t.updateLayout()}function Xn(t){const e=t.resumeFrom?.snapshot||t.snapshot;if(t.isLead()&&t.layout&&e&&t.hasListeners("didUpdate")){const{layoutBox:i,measuredBox:s}=t.layout,{animationType:n}=t.options,o=e.source!==t.layout.source;"size"===n?Cs(t=>{const s=o?e.measuredBox[t]:e.layoutBox[t],n=Ss(s);s.min=i[t].min,s.max=s.min+n}):co(n,e.layoutBox,i)&&Cs(s=>{const n=o?e.measuredBox[s]:e.layoutBox[s],r=Ss(i[s]);n.max=n.min+r,t.relativeTarget&&!t.currentAnimation&&(t.isProjectionDirty=!0,t.relativeTarget[s].max=t.relativeTarget[s].min+r)});const r={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};As(r,i,e.layoutBox);const a={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};o?As(a,t.applyTransform(s,!0),e.measuredBox):As(a,i,e.layoutBox);const h=!Rn(r);let l=!1;if(!t.resumeFrom){const s=t.getClosestProjectingParent();if(s&&!s.resumeFrom){const{snapshot:n,layout:o}=s;if(n&&o){const r={x:{min:0,max:0},y:{min:0,max:0}};Ms(r,e.layoutBox,n.layoutBox);const a={x:{min:0,max:0},y:{min:0,max:0}};Ms(a,i,o.layoutBox),Bn(r,a)||(l=!0),s.options.layoutRoot&&(t.relativeTarget=a,t.relativeTargetOrigin=r,t.relativeParent=s)}}}t.notifyListeners("didUpdate",{layout:i,snapshot:e,delta:a,layoutDelta:r,hasLayoutChanged:h,hasRelativeLayoutChanged:l})}else if(t.isLead()){const{onExitComplete:e}=t.options;e&&e()}t.options.transition=void 0}function Hn(t){t.parent&&(t.isProjecting()||(t.isProjectionDirty=t.parent.isProjectionDirty),t.isSharedProjectionDirty||(t.isSharedProjectionDirty=Boolean(t.isProjectionDirty||t.parent.isProjectionDirty||t.parent.isSharedProjectionDirty)),t.isTransformDirty||(t.isTransformDirty=t.parent.isTransformDirty))}function Gn(t){t.isProjectionDirty=t.isSharedProjectionDirty=t.isTransformDirty=!1}function qn(t){t.clearSnapshot()}function _n(t){t.clearMeasurements()}function Zn(t){t.isLayoutDirty=!1}function Jn(t){const{visualElement:e}=t.options;e&&e.getProps().onBeforeLayoutMeasure&&e.notify("BeforeLayoutMeasure"),t.resetTransform()}function Qn(t){t.finishAnimation(),t.targetDelta=t.relativeTarget=t.target=void 0,t.isProjectionDirty=!0}function to(t){t.resolveTargetDelta()}function eo(t){t.calcProjection()}function io(t){t.resetSkewAndRotation()}function so(t){t.removeLeadSnapshot()}function no(t,e,i){t.translate=Qt(e.translate,0,i),t.scale=Qt(e.scale,1,i),t.origin=e.origin,t.originPoint=e.originPoint}function oo(t,e,i,s){t.min=Qt(e.min,i.min,s),t.max=Qt(e.max,i.max,s)}function ro(t){return t.animationValues&&void 0!==t.animationValues.opacityExit}const ao={duration:.45,ease:[.4,0,.1,1]},ho=t=>"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(t),lo=ho("applewebkit/")&&!ho("chrome/")?Math.round:tt;function uo(t){t.min=lo(t.min),t.max=lo(t.max)}function co(t,e,i){return"position"===t||"preserve-aspect"===t&&(s=Fn(e),n=Fn(i),o=.2,!(Math.abs(s-n)<=o));var s,n,o}function po(t){return t!==t.root&&t.scroll?.wasRoot}const mo=zn({attachResizeListener:(t,e)=>xs(t,"resize",e),measureScroll:()=>({x:document.documentElement.scrollLeft||document.body.scrollLeft,y:document.documentElement.scrollTop||document.body.scrollTop}),checkIsScrollRoot:()=>!0}),fo={current:void 0},yo=zn({measureScroll:t=>({x:t.scrollLeft,y:t.scrollTop}),defaultParent:()=>{if(!fo.current){const t=new mo({});t.mount(window),t.setOptions({layoutScroll:!0}),fo.current=t}return fo.current},resetTransform:(t,e)=>{t.style.transform=void 0!==e?e:"none"},checkIsScrollRoot:t=>Boolean("fixed"===window.getComputedStyle(t).position)});function go(t,e){return e.max===e.min?0:t/(e.max-e.min)*100}const vo={correct:(t,e)=>{if(!e.target)return t;if("string"==typeof t){if(!a.test(t))return t;t=parseFloat(t)}return`${go(t,e.target.x)}% ${go(t,e.target.y)}%`}},xo={correct:(t,{treeScale:e,projectionDelta:i})=>{const s=t,n=_t.parse(t);if(n.length>5)return s;const o=_t.createTransformer(t),r="number"!=typeof n[0]?1:0,a=i.x.scale*e.x,h=i.y.scale*e.y;n[0+r]/=a,n[1+r]/=h;const l=Qt(a,h,.5);return"number"==typeof n[2+r]&&(n[2+r]/=l),"number"==typeof n[3+r]&&(n[3+r]/=l),o(n)}},To={current:null},Po={current:!1};const wo=new WeakMap;const So=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class bo{scrapeMotionValuesFromProps(t,e,i){return{}}constructor({parent:t,props:e,presenceContext:i,reducedMotionConfig:s,blockInitialAnimation:n,visualState:o},r={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=mi,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=kt.now();this.renderScheduledAtthis.bindToMotionValue(e,t)),Po.current||function(){if(Po.current=!0,T)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>To.current=t.matches;t.addEventListener("change",e),e()}else To.current=!1}(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||To.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){this.projection&&this.projection.unmount(),Et(this.notifyUpdate),Et(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const i=p.has(t);i&&this.onBindTransform&&this.onBindTransform();const s=e.on("change",e=>{this.latestValues[t]=e,this.props.onUpdate&&At.preRender(this.notifyUpdate),i&&this.projection&&(this.projection.isTransformDirty=!0)}),n=e.on("renderRequest",this.scheduleRender);let o;window.MotionCheckAppearSync&&(o=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{s(),n(),o&&o(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in S){const e=S[t];if(!e)continue;const{isEnabled:i,Feature:s}=e;if(!this.features[t]&&s&&i(this.props)&&(this.features[t]=new s(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const i=this.values.get(t);e!==i&&(i&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let i=this.values.get(t);return void 0===i&&void 0!==e&&(i=Zi(null===e?void 0:e,{owner:this}),this.addValue(t,i)),i}readValue(t,e){let i=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];var s;return null!=i&&("string"==typeof i&&(_(i)||J(i))?i=parseFloat(i):(s=i,!cs.find(Ii(s))&&_t.test(e)&&(i=Hi(t,e))),this.setBaseTarget(t,m(i)?i.get():i)),m(i)?i.get():i}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){const{initial:e}=this.props;let i;if("string"==typeof e||"object"==typeof e){const s=b(this.props,e,this.presenceContext?.custom);s&&(i=s[t])}if(e&&void 0!==i)return i;const s=this.getBaseTargetFromProps(this.props,t);return void 0===s||m(s)?void 0!==this.initialValues[t]&&void 0===i?void 0:this.baseTarget[t]:s}on(t,e){return this.events[t]||(this.events[t]=new nt),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class Ao extends bo{constructor(){super(...arguments),this.KeyframeResolver=qi}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:i}){delete e[t],delete i[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;m(t)&&(this.childSubscription=t.on("change",t=>{this.current&&(this.current.textContent=`${t}`)}))}}function Eo(t,{style:e,vars:i},s,n){const o=t.style;let r;for(r in e)o[r]=e[r];for(r in n?.applyProjectionStyles(o,s),i)o.setProperty(r,i[r])}class Vo extends Ao{constructor(){super(...arguments),this.type="html",this.renderInstance=Eo}readValueFromInstance(t,e){if(p.has(e))return this.projection?.isProjecting?ei(e):((t,e)=>{const{transform:i="none"}=getComputedStyle(t);return ii(i,e)})(t,e);{const s=(i=t,window.getComputedStyle(i)),n=(A(e)?s.getPropertyValue(e):s[e])||0;return"string"==typeof n?n.trim():n}var i}measureInstanceViewportBox(t,{transformPagePoint:e}){return Ys(t,e)}build(t,e,i){E(t,e,i.transformTemplate)}scrapeMotionValuesFromProps(t,e,i){return V(t,e,i)}}let Mo=!1;class Do extends X{componentDidMount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:i,layoutId:s}=this.props,{projection:n}=t;k(ko),n&&(e.group&&e.group.add(n),i&&i.register&&s&&i.register(n),Mo&&n.root.didUpdate(),n.addEventListener("animationComplete",()=>{this.safeToRemove()}),n.setOptions({...n.options,onExitComplete:()=>this.safeToRemove()})),Un.hasEverUpdated=!0}getSnapshotBeforeUpdate(t){const{layoutDependency:e,visualElement:i,drag:s,isPresent:n}=this.props,{projection:o}=i;return o?(o.isPresent=n,Mo=!0,s||t.layoutDependency!==e||void 0===e||t.isPresent!==n?o.willUpdate():this.safeToRemove(),t.isPresent!==n&&(n?o.promote():o.relegate()||At.postRender(()=>{const t=o.getStack();t&&t.members.length||this.safeToRemove()})),null):null}componentDidUpdate(){const{projection:t}=this.props.visualElement;t&&(t.root.didUpdate(),g.postRender(()=>{!t.currentAnimation&&t.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:i}=this.props,{projection:s}=t;s&&(s.scheduleCheckAfterUnmount(),e&&e.group&&e.group.remove(s),i&&i.deregister&&i.deregister(s))}safeToRemove(){const{safeToRemove:t}=this.props;t&&t()}render(){return null}}function Co(t){const[e,i]=function(t=!0){const e=W(M);if(null===e)return[!0,null];const{isPresent:i,onExitComplete:s,register:n}=e,o=$();z(()=>{if(t)return n(o)},[t]);const r=Y(()=>t&&s&&s(o),[o,s,t]);return!i&&s?[!1,r]:[!0]}(),s=W(D);return K(Do,{...t,layoutGroup:s,switchLayoutGroup:W(C),isPresent:e,safeToRemove:i})}const ko={borderRadius:{...vo,applyTo:["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"]},borderTopLeftRadius:vo,borderTopRightRadius:vo,borderBottomLeftRadius:vo,borderBottomRightRadius:vo,boxShadow:xo},Ro={pan:{Feature:class extends ds{constructor(){super(...arguments),this.removePointerDownListener=tt}onPointerDown(t){this.session=new qs(t,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:Xs(this.node)})}createPanHandlers(){const{onPanSessionStart:t,onPanStart:e,onPan:i,onPanEnd:s}=this.node.getProps();return{onSessionStart:cn(t),onStart:cn(e),onMove:i,onEnd:(t,e)=>{delete this.session,s&&At.postRender(()=>s(t,e))}}}mount(){this.removePointerDownListener=Ps(this.node.current,"pointerdown",t=>this.onPointerDown(t))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}},drag:{Feature:class extends ds{constructor(t){super(t),this.removeGroupControls=tt,this.removeListeners=tt,this.controls=new ln(t)}mount(){const{dragControls:t}=this.node.getProps();t&&(this.removeGroupControls=t.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||tt}unmount(){this.removeGroupControls(),this.removeListeners()}},ProjectionNode:yo,MeasureLayout:Co}},Lo={layout:{ProjectionNode:yo,MeasureLayout:Co}};function jo(t,e,i){const s=t.getProps();return b(s,e,void 0!==i?i:s.custom,t)}const Bo=t=>Array.isArray(t);function Fo(t,e,i){t.hasValue(e)?t.getValue(e).set(i):t.addValue(e,Zi(i))}function Io(t){return Bo(t)?t[t.length-1]||0:t}function Oo({protectedKeys:t,needsAnimating:e},i){const s=t.hasOwnProperty(i)&&!0!==e[i];return e[i]=!1,s}function Uo(t,e,{delay:i=0,transitionOverride:s,type:n}={}){let{transition:o=t.getDefaultTransition(),transitionEnd:r,...a}=e;s&&(o=s);const h=[],l=n&&t.animationState&&t.animationState.getState()[n];for(const e in a){const s=t.getValue(e,t.latestValues[e]??null),n=a[e];if(void 0===n||l&&Oo(l,e))continue;const r={delay:i,...Bi(o||{},e)},u=s.get();if(void 0!==u&&!s.isAnimating&&!Array.isArray(n)&&n===u&&!r.velocity)continue;let c=!1;if(window.MotionHandoffAnimation){const i=dn(t);if(i){const t=window.MotionHandoffAnimation(i,e,At);null!==t&&(r.startTime=t,c=!0)}}Hs(t,e),s.start(vs(e,s,n,t.shouldReduceMotion&&Fi.has(e)?{type:!1}:r,t,c));const d=s.animation;d&&h.push(d)}return r&&Promise.all(h).then(()=>{At.update(()=>{r&&function(t,e){const i=jo(t,e);let{transitionEnd:s={},transition:n={},...o}=i||{};o={...o,...s};for(const e in o)Fo(t,e,Io(o[e]))}(t,r)})}),h}function No(t,e,i={}){const s=jo(t,e,"exit"===i.type?t.presenceContext?.custom:void 0);let{transition:n=t.getDefaultTransition()||{}}=s||{};i.transitionOverride&&(n=i.transitionOverride);const o=s?()=>Promise.all(Uo(t,s,i)):()=>Promise.resolve(),r=t.variantChildren&&t.variantChildren.size?(s=0)=>{const{delayChildren:o=0,staggerChildren:r,staggerDirection:a}=n;return function(t,e,i=0,s=0,n=0,o=1,r){const a=[],h=t.variantChildren.size,l=(h-1)*n,u="function"==typeof s,c=u?t=>s(t,h):1===o?(t=0)=>t*n:(t=0)=>l-t*n;return Array.from(t.variantChildren).sort(Ko).forEach((t,n)=>{t.notify("AnimationStart",e),a.push(No(t,e,{...r,delay:i+(u?0:s)+c(n)}).then(()=>t.notify("AnimationComplete",e)))}),Promise.all(a)}(t,e,s,o,r,a,i)}:()=>Promise.resolve(),{when:a}=n;if(a){const[t,e]="beforeChildren"===a?[o,r]:[r,o];return t().then(()=>e())}return Promise.all([o(),r(i.delay)])}function Ko(t,e){return t.sortNodePosition(e)}function Wo(t,e){if(!Array.isArray(e))return!1;const i=e.length;if(i!==t.length)return!1;for(let s=0;sPromise.all(e.map(({animation:e,options:i})=>function(t,e,i={}){let s;if(t.notify("AnimationStart",e),Array.isArray(e)){const n=e.map(e=>No(t,e,i));s=Promise.all(n)}else if("string"==typeof e)s=No(t,e,i);else{const n="function"==typeof e?jo(t,e,i.custom):e;s=Promise.all(Uo(t,n,i))}return s.then(()=>{t.notify("AnimationComplete",e)})}(t,e,i)))}function Go(t){let e=Ho(t),i=Zo(),s=!0;const n=e=>(i,s)=>{const n=jo(t,s,"exit"===e?t.presenceContext?.custom:void 0);if(n){const{transition:t,transitionEnd:e,...s}=n;i={...i,...s,...e}}return i};function o(o){const{props:r}=t,a=zo(t.parent)||{},h=[],l=new Set;let u={},c=1/0;for(let e=0;ec&&f,T=!1;const P=Array.isArray(m)?m:[m];let w=P.reduce(n(d),{});!1===y&&(w={});const{prevResolvedValues:S={}}=p,b={...S,...w},A=e=>{x=!0,l.has(e)&&(T=!0,l.delete(e)),p.needsAnimating[e]=!0;const i=t.getValue(e);i&&(i.liveStyle=!1)};for(const t in b){const e=w[t],i=S[t];if(u.hasOwnProperty(t))continue;let s=!1;s=Bo(e)&&Bo(i)?!Wo(e,i):e!==i,s?null!=e?A(t):l.add(t):void 0!==e&&l.has(t)?A(t):p.protectedKeys[t]=!0}p.prevProp=m,p.prevResolvedValues=w,p.isActive&&(u={...u,...w}),s&&t.blockInitialAnimation&&(x=!1);x&&(!(g&&v)||T)&&h.push(...P.map(t=>({animation:t,options:{type:d}})))}if(l.size){const e={};if("boolean"!=typeof r.initial){const i=jo(t,Array.isArray(r.initial)?r.initial[0]:r.initial);i&&i.transition&&(e.transition=i.transition)}l.forEach(i=>{const s=t.getBaseTarget(i),n=t.getValue(i);n&&(n.liveStyle=!0),e[i]=s??null}),h.push({animation:e})}let d=Boolean(h.length);return!s||!1!==r.initial&&r.initial!==r.animate||t.manuallyAnimateOnMount||(d=!1),s=!1,d?e(h):Promise.resolve()}return{animateChanges:o,setActive:function(e,s){if(i[e].isActive===s)return Promise.resolve();t.variantChildren?.forEach(t=>t.animationState?.setActive(e,s)),i[e].isActive=s;const n=o(e);for(const t in i)i[t].protectedKeys={};return n},setAnimateFunction:function(i){e=i(t)},getState:()=>i,reset:()=>{i=Zo(),s=!0}}}function qo(t,e){return"string"==typeof e?e!==t:!!Array.isArray(e)&&!Wo(e,t)}function _o(t=!1){return{isActive:t,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function Zo(){return{animate:_o(!0),whileInView:_o(),whileHover:_o(),whileTap:_o(),whileDrag:_o(),whileFocus:_o(),exit:_o()}}let Jo=0;const Qo={animation:{Feature:class extends ds{constructor(t){super(t),t.animationState||(t.animationState=Go(t))}updateAnimationControlsSubscription(){const{animate:t}=this.node.getProps();j(t)&&(this.unmountControls=t.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:t}=this.node.getProps(),{animate:e}=this.node.prevProps||{};t!==e&&this.updateAnimationControlsSubscription()}unmount(){this.node.animationState.reset(),this.unmountControls?.()}}},exit:{Feature:class extends ds{constructor(){super(...arguments),this.id=Jo++}update(){if(!this.node.presenceContext)return;const{isPresent:t,onExitComplete:e}=this.node.presenceContext,{isPresent:i}=this.node.prevPresenceContext||{};if(!this.node.animationState||t===i)return;const s=this.node.animationState.setActive("exit",!t);e&&!t&&s.then(()=>{e(this.id)})}mount(){const{register:t,onExitComplete:e}=this.node.presenceContext||{};e&&e(this.id),t&&(this.unmount=t(this.id))}unmount(){}}}};function tr(t,e,i){const{props:s}=t;t.animationState&&s.whileHover&&t.animationState.setActive("whileHover","Start"===i);const n=s["onHover"+i];n&&At.postRender(()=>n(e,Ts(e)))}function er(t,e,i){const{props:s}=t;if(t.current instanceof HTMLButtonElement&&t.current.disabled)return;t.animationState&&s.whileTap&&t.animationState.setActive("whileTap","Start"===i);const n=s["onTap"+("End"===i?"":i)];n&&At.postRender(()=>n(e,Ts(e)))}const ir=new WeakMap,sr=new WeakMap,nr=t=>{const e=ir.get(t.target);e&&e(t)},or=t=>{t.forEach(nr)};function rr(t,e,i){const s=function({root:t,...e}){const i=t||document;sr.has(i)||sr.set(i,{});const s=sr.get(i),n=JSON.stringify(e);return s[n]||(s[n]=new IntersectionObserver(or,{root:t,...e})),s[n]}(e);return ir.set(t,i),s.observe(t),()=>{ir.delete(t),s.unobserve(t)}}const ar={some:0,all:1};const hr={inView:{Feature:class extends ds{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){this.unmount();const{viewport:t={}}=this.node.getProps(),{root:e,margin:i,amount:s="some",once:n}=t,o={root:e?e.current:void 0,rootMargin:i,threshold:"number"==typeof s?s:ar[s]};return rr(this.node.current,o,t=>{const{isIntersecting:e}=t;if(this.isInView===e)return;if(this.isInView=e,n&&!e&&this.hasEnteredView)return;e&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",e);const{onViewportEnter:i,onViewportLeave:s}=this.node.getProps(),o=e?i:s;o&&o(t)})}mount(){this.startObserver()}update(){if("undefined"==typeof IntersectionObserver)return;const{props:t,prevProps:e}=this.node;["amount","margin","root"].some(function({viewport:t={}},{viewport:e={}}={}){return i=>t[i]!==e[i]}(t,e))&&this.startObserver()}unmount(){}}},tap:{Feature:class extends ds{mount(){const{current:t}=this.node;t&&(this.unmount=ls(t,(t,e)=>(er(this.node,e,"Start"),(t,{success:e})=>er(this.node,t,e?"End":"Cancel")),{useGlobalTarget:this.node.props.globalTapTarget}))}unmount(){}}},focus:{Feature:class extends ds{constructor(){super(...arguments),this.isActive=!1}onFocus(){let t=!1;try{t=this.node.current.matches(":focus-visible")}catch(e){t=!0}t&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){this.isActive&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=it(xs(this.node.current,"focus",()=>this.onFocus()),xs(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}},hover:{Feature:class extends ds{mount(){const{current:t}=this.node;t&&(this.unmount=function(t,e,i={}){const[s,n,o]=ts(t,i),r=t=>{if(!es(t))return;const{target:i}=t,s=e(i,t);if("function"!=typeof s||!i)return;const o=t=>{es(t)&&(s(t),i.removeEventListener("pointerleave",o))};i.addEventListener("pointerleave",o,n)};return s.forEach(t=>{t.addEventListener("pointerenter",r,n)}),o}(t,(t,e)=>(tr(this.node,e,"Start"),t=>tr(this.node,t,"End"))))}unmount(){}}}},lr=new Set(["baseFrequency","diffuseConstant","kernelMatrix","kernelUnitLength","keySplines","keyTimes","limitingConeAngle","markerHeight","markerWidth","numOctaves","targetX","targetY","surfaceScale","specularConstant","specularExponent","stdDeviation","tableValues","viewBox","gradientTransform","pathLength","startOffset","textLength","lengthAdjust"]);class ur extends Ao{constructor(){super(...arguments),this.type="svg",this.isSVGTag=!1,this.measureInstanceViewportBox=Ds}getBaseTargetFromProps(t,e){return t[e]}readValueFromInstance(t,e){if(p.has(e)){const t=Xi(e);return t&&t.default||0}return e=lr.has(e)?e:F(e),t.getAttribute(e)}scrapeMotionValuesFromProps(t,e,i){return I(t,e,i)}build(t,e,i){O(t,e,this.isSVGTag,i.transformTemplate,i.style)}renderInstance(t,e,i,s){!function(t,e,i,s){Eo(t,e,void 0,s);for(const i in e.attrs)t.setAttribute(lr.has(i)?i:F(i),e.attrs[i])}(t,e,0,s)}mount(t){this.isSVGTag=U(t.tagName),super.mount(t)}}const cr={renderer:(t,e)=>N(t)?new ur(e):new Vo(e,{allowProjection:t!==H}),...Qo,...hr},dr={...cr,...Ro,...Lo};export{dr as domMax}; diff --git a/node_modules/framer-motion/dist/size-rollup-m.js b/node_modules/framer-motion/dist/size-rollup-m.js new file mode 100644 index 00000000..61bc4796 --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-m.js @@ -0,0 +1 @@ +import{jsxs as t,jsx as e}from"react/jsx-runtime";import{createContext as n,useContext as r,useMemo as o,useCallback as a,useLayoutEffect as s,useEffect as i,useRef as c,useInsertionEffect as l,forwardRef as u,Fragment as f,createElement as d}from"react";const p={},m=n({}),g=n({strict:!1}),y=n({transformPagePoint:t=>t,isStatic:!1,reducedMotion:"never"}),h=n({});function v(t){return null!==t&&"object"==typeof t&&"function"==typeof t.start}function w(t){return"string"==typeof t||Array.isArray(t)}const b=["initial","animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"];function S(t){return v(t.animate)||b.some(e=>w(t[e]))}function x(t){const{initial:e,animate:n}=function(t,e){if(S(t)){const{initial:e,animate:n}=t;return{initial:!1===e||w(e)?e:void 0,animate:w(n)?n:void 0}}return!1!==t.inherit?e:{}}(t,r(h));return o(()=>({initial:e,animate:n}),[M(e),M(n)])}function M(t){return Array.isArray(t)?t.join(" "):t}const P="undefined"!=typeof window,T={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},k={};for(const t in T)k[t]={isEnabled:e=>T[t].some(t=>!!e[t])};const E=Symbol.for("motionComponentSymbol");function O(t){return t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"current")}function V(t,e,n){return a(r=>{r&&t.onMount&&t.onMount(r),e&&(r?e.mount(r):e.unmount()),n&&("function"==typeof n?n(r):O(n)&&(n.current=r))},[e])}const W=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],R={value:null,addProjectionMetrics:null};function C(t,e){let n=!1,r=!0;const o={delta:0,timestamp:0,isProcessing:!1},a=()=>n=!0,s=W.reduce((t,n)=>(t[n]=function(t,e){let n=new Set,r=new Set,o=!1,a=!1;const s=new WeakSet;let i={delta:0,timestamp:0,isProcessing:!1},c=0;function l(e){s.has(e)&&(u.schedule(e),t()),c++,e(i)}const u={schedule:(t,e=!1,a=!1)=>{const i=a&&o?n:r;return e&&s.add(t),i.has(t)||i.add(t),t},cancel:t=>{r.delete(t),s.delete(t)},process:t=>{i=t,o?a=!0:(o=!0,[n,r]=[r,n],n.forEach(l),e&&R.value&&R.value.frameloop[e].push(c),c=0,n.clear(),o=!1,a&&(a=!1,u.process(t)))}};return u}(a,e?n:void 0),t),{}),{setup:i,read:c,resolveKeyframes:l,preUpdate:u,update:f,preRender:d,render:m,postRender:g}=s,y=()=>{const a=p.useManualTiming?o.timestamp:performance.now();n=!1,p.useManualTiming||(o.delta=r?1e3/60:Math.max(Math.min(a-o.timestamp,40),1)),o.timestamp=a,o.isProcessing=!0,i.process(o),c.process(o),l.process(o),u.process(o),f.process(o),d.process(o),m.process(o),g.process(o),o.isProcessing=!1,n&&e&&(r=!1,t(y))};return{schedule:W.reduce((e,a)=>{const i=s[a];return e[a]=(e,a=!1,s=!1)=>(n||(n=!0,r=!0,o.isProcessing||t(y)),i.schedule(e,a,s)),e},{}),cancel:t=>{for(let e=0;ee=>"string"==typeof e&&e.startsWith(t))("--"),A={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},j={...A,transform:t=>((t,e,n)=>n>e?e:n({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),X=I("deg"),Y=I("%"),$=I("px"),F=(()=>({...Y,parse:t=>Y.parse(t)/100,transform:t=>Y.transform(100*t)}))(),D=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],H=(()=>new Set(D))(),Z={...A,transform:Math.round},z={borderWidth:$,borderTopWidth:$,borderRightWidth:$,borderBottomWidth:$,borderLeftWidth:$,borderRadius:$,radius:$,borderTopLeftRadius:$,borderTopRightRadius:$,borderBottomRightRadius:$,borderBottomLeftRadius:$,width:$,maxWidth:$,height:$,maxHeight:$,top:$,right:$,bottom:$,left:$,padding:$,paddingTop:$,paddingRight:$,paddingBottom:$,paddingLeft:$,margin:$,marginTop:$,marginRight:$,marginBottom:$,marginLeft:$,backgroundPositionX:$,backgroundPositionY:$,...{rotate:X,rotateX:X,rotateY:X,rotateZ:X,scale:B,scaleX:B,scaleY:B,scaleZ:B,skew:X,skewX:X,skewY:X,distance:$,translateX:$,translateY:$,translateZ:$,x:$,y:$,z:$,perspective:$,transformPerspective:$,opacity:j,originX:F,originY:F,originZ:$},zIndex:Z,fillOpacity:j,strokeOpacity:j,numOctaves:Z},N=(t,e)=>e&&"number"==typeof t?e.transform(t):t,{schedule:U,cancel:q}=C(queueMicrotask,!1),K=t=>Boolean(t&&t.getVelocity),_="data-"+"framerAppearId".replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase();const G=n(null),J=n({}),Q=P?s:i;function tt(t,e,n,o,a){const{visualElement:s}=r(h),u=r(g),f=r(G),d=r(y).reducedMotion,p=c(null);o=o||u.renderer,!p.current&&o&&(p.current=o(t,{visualState:e,parent:s,props:n,presenceContext:f,blockInitialAnimation:!!f&&!1===f.initial,reducedMotionConfig:d}));const m=p.current,v=r(J);!m||m.projection||!a||"html"!==m.type&&"svg"!==m.type||function(t,e,n,r){const{layoutId:o,layout:a,drag:s,dragConstraints:i,layoutScroll:c,layoutRoot:l,layoutCrossfade:u}=e;t.projection=new n(t.latestValues,e["data-framer-portal-id"]?void 0:et(t.parent)),t.projection.setOptions({layoutId:o,layout:a,alwaysMeasureLayout:Boolean(s)||i&&O(i),visualElement:t,animationType:"string"==typeof a?a:"both",initialPromotionConfig:r,crossfade:u,layoutScroll:c,layoutRoot:l})}(p.current,n,a,v);const w=c(!1);l(()=>{m&&w.current&&m.update(n,f)});const b=n[_],S=c(Boolean(b)&&!window.MotionHandoffIsComplete?.(b)&&window.MotionHasOptimisedAnimation?.(b));return Q(()=>{m&&(w.current=!0,window.MotionIsMounted=!0,m.updateFeatures(),U.render(m.render),S.current&&m.animationState&&m.animationState.animateChanges())}),i(()=>{m&&(!S.current&&m.animationState&&m.animationState.animateChanges(),S.current&&(queueMicrotask(()=>{window.MotionHandoffMarkAsComplete?.(b)}),S.current=!1))}),m}function et(t){if(t)return!1!==t.options.allowProjection?t.projection:et(t.parent)}function nt({preloadedFeatures:n,createVisualElement:o,useRender:a,useVisualState:s,Component:i}){function c(n,c){let l;const u={...r(y),...n,layoutId:rt(n)},{isStatic:f}=u,d=x(n),p=s(n,f);if(!f&&P){r(g).strict;const t=function(t){const{drag:e,layout:n}=k;if(!e&&!n)return{};const r={...e,...n};return{MeasureLayout:e?.isEnabled(t)||n?.isEnabled(t)?r.MeasureLayout:void 0,ProjectionNode:r.ProjectionNode}}(u);l=t.MeasureLayout,d.visualElement=tt(i,p,u,o,t.ProjectionNode)}return t(h.Provider,{value:d,children:[l&&d.visualElement?e(l,{visualElement:d.visualElement,...u}):null,a(i,n,V(p,d.visualElement,c),p,f,d.visualElement)]})}n&&function(t){for(const e in t)k[e]={...k[e],...t[e]}}(n),c.displayName=`motion.${"string"==typeof i?i:`create(${i.displayName??i.name??""})`}`;const l=u(c);return l[E]=i,l}function rt({layoutId:t}){const e=r(m).id;return e&&void 0!==t?e+"-"+t:t}const ot={};function at(t,{layout:e,layoutId:n}){return H.has(t)||t.startsWith("origin")||(e||void 0!==n)&&(!!ot[t]||"opacity"===t)}const st={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},it=D.length;function ct(t,e,n){const{style:r,vars:o,transformOrigin:a}=t;let s=!1,i=!1;for(const t in e){const n=e[t];if(H.has(t))s=!0;else if(L(t))o[t]=n;else{const e=N(n,z[t]);t.startsWith("origin")?(i=!0,a[t]=e):r[t]=e}}if(e.transform||(s||n?r.transform=function(t,e,n){let r="",o=!0;for(let a=0;a({style:{},transform:{},transformOrigin:{},vars:{}});function ut(t,e,n){for(const r in e)K(e[r])||at(r,n)||(t[r]=e[r])}function ft(t,e){const n={};return ut(n,t.style||{},t),Object.assign(n,function({transformTemplate:t},e){return o(()=>{const n={style:{},transform:{},transformOrigin:{},vars:{}};return ct(n,e,t),Object.assign({},n.vars,n.style)},[e])}(t,e)),n}function dt(t,e){const n={},r=ft(t,e);return t.drag&&!1!==t.dragListener&&(n.draggable=!1,r.userSelect=r.WebkitUserSelect=r.WebkitTouchCallout="none",r.touchAction=!0===t.drag?"none":"pan-"+("x"===t.drag?"y":"x")),void 0===t.tabIndex&&(t.onTap||t.onTapStart||t.whileTap)&&(n.tabIndex=0),n.style=r,n}const pt={offset:"stroke-dashoffset",array:"stroke-dasharray"},mt={offset:"strokeDashoffset",array:"strokeDasharray"};function gt(t,{attrX:e,attrY:n,attrScale:r,pathLength:o,pathSpacing:a=1,pathOffset:s=0,...i},c,l,u){if(ct(t,i,l),c)return void(t.style.viewBox&&(t.attrs.viewBox=t.style.viewBox));t.attrs=t.style,t.style={};const{attrs:f,style:d}=t;f.transform&&(d.transform=f.transform,delete f.transform),(d.transform||f.transformOrigin)&&(d.transformOrigin=f.transformOrigin??"50% 50%",delete f.transformOrigin),d.transform&&(d.transformBox=u?.transformBox??"fill-box",delete f.transformBox),void 0!==e&&(f.x=e),void 0!==n&&(f.y=n),void 0!==r&&(f.scale=r),void 0!==o&&function(t,e,n=1,r=0,o=!0){t.pathLength=1;const a=o?pt:mt;t[a.offset]=$.transform(-r);const s=$.transform(e),i=$.transform(n);t[a.array]=`${s} ${i}`}(f,o,a,s,!1)}const yt=()=>({style:{},transform:{},transformOrigin:{},vars:{},attrs:{}});function ht(t,e,n,r){const a=o(()=>{const n={style:{},transform:{},transformOrigin:{},vars:{},attrs:{}};var o;return gt(n,e,"string"==typeof(o=r)&&"svg"===o.toLowerCase(),t.transformTemplate,t.style),{...n.attrs,style:{...n.style}}},[e]);if(t.style){const e={};ut(e,t.style,t),a.style={...e,...a.style}}return a}const vt=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","ignoreStrict","viewport"]);function wt(t){return t.startsWith("while")||t.startsWith("drag")&&"draggable"!==t||t.startsWith("layout")||t.startsWith("onTap")||t.startsWith("onPan")||t.startsWith("onLayout")||vt.has(t)}let bt=t=>!wt(t);try{"function"==typeof(St=require("@emotion/is-prop-valid").default)&&(bt=t=>t.startsWith("on")?!wt(t):St(t))}catch{}var St;const xt=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function Mt(t){return"string"==typeof t&&!t.includes("-")&&!!(xt.indexOf(t)>-1||/[A-Z]/u.test(t))}function Pt(t=!1){return(e,n,r,{latestValues:a},s)=>{const i=(Mt(e)?ht:dt)(n,a,s,e),c=function(t,e,n){const r={};for(const o in t)"values"===o&&"object"==typeof t.values||(bt(o)||!0===n&&wt(o)||!e&&!wt(o)||t.draggable&&o.startsWith("onDrag"))&&(r[o]=t[o]);return r}(n,"string"==typeof e,t),l=e!==f?{...c,...i,ref:r}:{},{children:u}=n,p=o(()=>K(u)?u.get():u,[u]);return d(e,{...l,children:p})}}function Tt(t){const e=[{},{}];return t?.values.forEach((t,n)=>{e[0][n]=t.get(),e[1][n]=t.getVelocity()}),e}function kt(t,e,n,r){if("function"==typeof e){const[o,a]=Tt(r);e=e(void 0!==n?n:t.custom,o,a)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[o,a]=Tt(r);e=e(void 0!==n?n:t.custom,o,a)}return e}function Et(t){return K(t)?t.get():t}const Ot=t=>(e,n)=>{const o=r(h),a=r(G),s=()=>function({scrapeMotionValuesFromProps:t,createRenderState:e},n,r,o){return{latestValues:Vt(n,r,o,t),renderState:e()}}(t,e,o,a);return n?s():function(t){const e=c(null);return null===e.current&&(e.current=t()),e.current}(s)};function Vt(t,e,n,r){const o={},a=r(t,{});for(const t in a)o[t]=Et(a[t]);let{initial:s,animate:i}=t;const c=S(t),l=function(t){return Boolean(S(t)||t.variants)}(t);e&&l&&!c&&!1!==t.inherit&&(void 0===s&&(s=e.initial),void 0===i&&(i=e.animate));let u=!!n&&!1===n.initial;u=u||!1===s;const f=u?i:s;if(f&&"boolean"!=typeof f&&!v(f)){const e=Array.isArray(f)?f:[f];for(let n=0;n{e[0][n]=t.get(),e[1][n]=t.getVelocity()}),e}function g(t,e,n,i){if("function"==typeof e){const[s,o]=y(i);e=e(void 0!==n?n:t.custom,s,o)}if("string"==typeof e&&(e=t.variants&&t.variants[e]),"function"==typeof e){const[s,o]=y(i);e=e(void 0!==n?n:t.custom,s,o)}return e}function v(t,e,n){const i=t.getProps();return g(i,e,void 0!==n?n:i.custom,t)}function x(t,e){-1===t.indexOf(e)&&t.push(e)}function T(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}const w=(t,e,n)=>n>e?e:n/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(t);function b(t){return"object"==typeof t&&null!==t}const A=t=>/^0[^.\s]+$/u.test(t);function E(t){let e;return()=>(void 0===e&&(e=t()),e)}const V=t=>t,M=(t,e)=>n=>e(t(n)),D=(...t)=>t.reduce(M),C=(t,e,n)=>{const i=e-t;return 0===i?1:(n-t)/i};class k{constructor(){this.subscriptions=[]}add(t){return x(this.subscriptions,t),()=>T(this.subscriptions,t)}notify(t,e,n){const i=this.subscriptions.length;if(i)if(1===i)this.subscriptions[0](t,e,n);else for(let s=0;s1e3*t,L=t=>t/1e3;function j(t,e){return e?t*(1e3/e):0}const B=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function F(t,e,n,i){if(t===e&&n===i)return V;const s=e=>function(t,e,n,i,s){let o,r,a=0;do{r=e+(n-e)/2,o=B(r,i,s)-t,o>0?n=r:e=r}while(Math.abs(o)>1e-7&&++a<12);return r}(e,0,1,t,n);return t=>0===t||1===t?t:B(s(t),e,i)}const O=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,I=t=>e=>1-t(1-e),U=F(.33,1.53,.69,.99),N=I(U),W=O(N),$=t=>(t*=2)<1?.5*N(t):.5*(2-Math.pow(2,-10*(t-1))),Y=t=>1-Math.sin(Math.acos(t)),X=I(Y),K=O(Y),z=F(.42,0,1,1),H=F(0,0,.58,1),q=F(.42,0,.58,1),G=t=>Array.isArray(t)&&"number"==typeof t[0],Z={linear:V,easeIn:z,easeInOut:q,easeOut:H,circIn:Y,circInOut:K,circOut:X,backIn:N,backInOut:W,backOut:U,anticipate:$},_=t=>{if(G(t)){t.length;const[e,n,i,s]=t;return F(e,n,i,s)}return"string"==typeof t?Z[t]:t},J=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],Q={value:null,addProjectionMetrics:null};function tt(t,e){let n=!1,i=!0;const s={delta:0,timestamp:0,isProcessing:!1},o=()=>n=!0,r=J.reduce((t,n)=>(t[n]=function(t,e){let n=new Set,i=new Set,s=!1,o=!1;const r=new WeakSet;let a={delta:0,timestamp:0,isProcessing:!1},l=0;function h(e){r.has(e)&&(u.schedule(e),t()),l++,e(a)}const u={schedule:(t,e=!1,o=!1)=>{const a=o&&s?n:i;return e&&r.add(t),a.has(t)||a.add(t),t},cancel:t=>{i.delete(t),r.delete(t)},process:t=>{a=t,s?o=!0:(s=!0,[n,i]=[i,n],n.forEach(h),e&&Q.value&&Q.value.frameloop[e].push(l),l=0,n.clear(),s=!1,o&&(o=!1,u.process(t)))}};return u}(o,e?n:void 0),t),{}),{setup:a,read:l,resolveKeyframes:h,preUpdate:u,update:c,preRender:d,render:p,postRender:m}=r,f=()=>{const o=P.useManualTiming?s.timestamp:performance.now();n=!1,P.useManualTiming||(s.delta=i?1e3/60:Math.max(Math.min(o-s.timestamp,40),1)),s.timestamp=o,s.isProcessing=!0,a.process(s),l.process(s),h.process(s),u.process(s),c.process(s),d.process(s),p.process(s),m.process(s),s.isProcessing=!1,n&&e&&(i=!1,t(f))};return{schedule:J.reduce((e,o)=>{const a=r[o];return e[o]=(e,o=!1,r=!1)=>(n||(n=!0,i=!0,s.isProcessing||t(f)),a.schedule(e,o,r)),e},{}),cancel:t=>{for(let e=0;e(void 0===ot&&at.set(it.isProcessing||P.useManualTiming?it.timestamp:performance.now()),ot),set:t=>{ot=t,queueMicrotask(rt)}},lt=t=>e=>"string"==typeof e&&e.startsWith(t),ht=lt("--"),ut=lt("var(--"),ct=t=>!!ut(t)&&dt.test(t.split("/*")[0].trim()),dt=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,pt={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},mt={...pt,transform:t=>w(0,1,t)},ft={...pt,default:1},yt=t=>Math.round(1e5*t)/1e5,gt=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const vt=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,xt=(t,e)=>n=>Boolean("string"==typeof n&&vt.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),Tt=(t,e,n)=>i=>{if("string"!=typeof i)return i;const[s,o,r,a]=i.match(gt);return{[t]:parseFloat(s),[e]:parseFloat(o),[n]:parseFloat(r),alpha:void 0!==a?parseFloat(a):1}},wt={...pt,transform:t=>Math.round((t=>w(0,255,t))(t))},Pt={test:xt("rgb","red"),parse:Tt("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:i=1})=>"rgba("+wt.transform(t)+", "+wt.transform(e)+", "+wt.transform(n)+", "+yt(mt.transform(i))+")"};const St={test:xt("#"),parse:function(t){let e="",n="",i="",s="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),i=t.substring(5,7),s=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),i=t.substring(3,4),s=t.substring(4,5),e+=e,n+=n,i+=i,s+=s),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(i,16),alpha:s?parseInt(s,16)/255:1}},transform:Pt.transform},bt=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),At=bt("deg"),Et=bt("%"),Vt=bt("px"),Mt=bt("vh"),Dt=bt("vw"),Ct=(()=>({...Et,parse:t=>Et.parse(t)/100,transform:t=>Et.transform(100*t)}))(),kt={test:xt("hsl","hue"),parse:Tt("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:i=1})=>"hsla("+Math.round(t)+", "+Et.transform(yt(e))+", "+Et.transform(yt(n))+", "+yt(mt.transform(i))+")"},Rt={test:t=>Pt.test(t)||St.test(t)||kt.test(t),parse:t=>Pt.test(t)?Pt.parse(t):kt.test(t)?kt.parse(t):St.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?Pt.transform(t):kt.transform(t),getAnimatableNone:t=>{const e=Rt.parse(t);return e.alpha=0,Rt.transform(e)}},Lt=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const jt="number",Bt="color",Ft=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Ot(t){const e=t.toString(),n=[],i={color:[],number:[],var:[]},s=[];let o=0;const r=e.replace(Ft,t=>(Rt.test(t)?(i.color.push(o),s.push(Bt),n.push(Rt.parse(t))):t.startsWith("var(")?(i.var.push(o),s.push("var"),n.push(t)):(i.number.push(o),s.push(jt),n.push(parseFloat(t))),++o,"${}")).split("${}");return{values:n,split:r,indexes:i,types:s}}function It(t){return Ot(t).values}function Ut(t){const{split:e,types:n}=Ot(t),i=e.length;return t=>{let s="";for(let o=0;o"number"==typeof t?0:Rt.test(t)?Rt.getAnimatableNone(t):t;const Wt={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(gt)?.length||0)+(t.match(Lt)?.length||0)>0},parse:It,createTransformer:Ut,getAnimatableNone:function(t){const e=It(t);return Ut(t)(e.map(Nt))}};function $t(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function Yt(t,e){return n=>n>0?e:t}const Xt=(t,e,n)=>t+(e-t)*n,Kt=(t,e,n)=>{const i=t*t,s=n*(e*e-i)+i;return s<0?0:Math.sqrt(s)},zt=[St,Pt,kt];function Ht(t){const e=(n=t,zt.find(t=>t.test(n)));var n;if(!Boolean(e))return!1;let i=e.parse(t);return e===kt&&(i=function({hue:t,saturation:e,lightness:n,alpha:i}){t/=360,n/=100;let s=0,o=0,r=0;if(e/=100){const i=n<.5?n*(1+e):n+e-n*e,a=2*n-i;s=$t(a,i,t+1/3),o=$t(a,i,t),r=$t(a,i,t-1/3)}else s=o=r=n;return{red:Math.round(255*s),green:Math.round(255*o),blue:Math.round(255*r),alpha:i}}(i)),i}const qt=(t,e)=>{const n=Ht(t),i=Ht(e);if(!n||!i)return Yt(t,e);const s={...n};return t=>(s.red=Kt(n.red,i.red,t),s.green=Kt(n.green,i.green,t),s.blue=Kt(n.blue,i.blue,t),s.alpha=Xt(n.alpha,i.alpha,t),Pt.transform(s))},Gt=new Set(["none","hidden"]);function Zt(t,e){return n=>Xt(t,e,n)}function _t(t){return"number"==typeof t?Zt:"string"==typeof t?ct(t)?Yt:Rt.test(t)?qt:te:Array.isArray(t)?Jt:"object"==typeof t?Rt.test(t)?qt:Qt:Yt}function Jt(t,e){const n=[...t],i=n.length,s=t.map((t,n)=>_t(t)(t,e[n]));return t=>{for(let e=0;e{for(const e in i)n[e]=i[e](t);return n}}const te=(t,e)=>{const n=Wt.createTransformer(e),i=Ot(t),s=Ot(e);return i.indexes.var.length===s.indexes.var.length&&i.indexes.color.length===s.indexes.color.length&&i.indexes.number.length>=s.indexes.number.length?Gt.has(t)&&!s.values.length||Gt.has(e)&&!i.values.length?function(t,e){return Gt.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}(t,e):D(Jt(function(t,e){const n=[],i={color:0,var:0,number:0};for(let s=0;s{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>et.update(e,t),stop:()=>nt(e),now:()=>it.isProcessing?it.timestamp:at.now()}},ie=(t,e,n=10)=>{let i="";const s=Math.max(Math.round(e/n),2);for(let e=0;e=se?1/0:e}function re(t,e,n){const i=Math.max(e-5,0);return j(n-t(i),e-i)}const ae=100,le=10,he=1,ue=0,ce=800,de=.3,pe=.3,me={granular:.01,default:2},fe={granular:.005,default:.5},ye=.01,ge=10,ve=.05,xe=1,Te=.001;function we({duration:t=ce,bounce:e=de,velocity:n=ue,mass:i=he}){let s,o,r=1-e;r=w(ve,xe,r),t=w(ye,ge,L(t)),r<1?(s=e=>{const i=e*r,s=i*t,o=i-n,a=Se(e,r),l=Math.exp(-s);return Te-o/a*l},o=e=>{const i=e*r*t,o=i*n+n,a=Math.pow(r,2)*Math.pow(e,2)*t,l=Math.exp(-i),h=Se(Math.pow(e,2),r);return(-s(e)+Te>0?-1:1)*((o-a)*l)/h}):(s=e=>Math.exp(-e*t)*((e-n)*t+1)-.001,o=e=>Math.exp(-e*t)*(t*t*(n-e)));const a=function(t,e,n){let i=n;for(let n=1;nvoid 0!==t[e])}function Ve(t=pe,e=de){const n="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:i,restDelta:s}=n;const o=n.keyframes[0],r=n.keyframes[n.keyframes.length-1],a={done:!1,value:o},{stiffness:l,damping:h,mass:u,duration:c,velocity:d,isResolvedFromDuration:p}=function(t){let e={velocity:ue,stiffness:ae,damping:le,mass:he,isResolvedFromDuration:!1,...t};if(!Ee(t,Ae)&&Ee(t,be))if(t.visualDuration){const n=t.visualDuration,i=2*Math.PI/(1.2*n),s=i*i,o=2*w(.05,1,1-(t.bounce||0))*Math.sqrt(s);e={...e,mass:he,stiffness:s,damping:o}}else{const n=we(t);e={...e,...n,mass:he},e.isResolvedFromDuration=!0}return e}({...n,velocity:-L(n.velocity||0)}),m=d||0,f=h/(2*Math.sqrt(l*u)),y=r-o,g=L(Math.sqrt(l/u)),v=Math.abs(y)<5;let x;if(i||(i=v?me.granular:me.default),s||(s=v?fe.granular:fe.default),f<1){const t=Se(g,f);x=e=>{const n=Math.exp(-f*g*e);return r-n*((m+f*g*y)/t*Math.sin(t*e)+y*Math.cos(t*e))}}else if(1===f)x=t=>r-Math.exp(-g*t)*(y+(m+g*y)*t);else{const t=g*Math.sqrt(f*f-1);x=e=>{const n=Math.exp(-f*g*e),i=Math.min(t*e,300);return r-n*((m+f*g*y)*Math.sinh(i)+t*y*Math.cosh(i))/t}}const T={calculatedDuration:p&&c||null,next:t=>{const e=x(t);if(p)a.done=t>=c;else{let n=0===t?m:0;f<1&&(n=0===t?R(m):re(x,t,e));const o=Math.abs(n)<=i,l=Math.abs(r-e)<=s;a.done=o&&l}return a.value=a.done?r:e,a},toString:()=>{const t=Math.min(oe(T),se),e=ie(e=>T.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return T}function Me({keyframes:t,velocity:e=0,power:n=.8,timeConstant:i=325,bounceDamping:s=10,bounceStiffness:o=500,modifyTarget:r,min:a,max:l,restDelta:h=.5,restSpeed:u}){const c=t[0],d={done:!1,value:c},p=t=>void 0===a?l:void 0===l||Math.abs(a-t)-m*Math.exp(-t/i),v=t=>y+g(t),x=t=>{const e=g(t),n=v(t);d.done=Math.abs(e)<=h,d.value=d.done?y:n};let T,w;const P=t=>{var e;(e=d.value,void 0!==a&&el)&&(T=t,w=Ve({keyframes:[d.value,p(d.value)],velocity:re(v,t,d.value),damping:s,stiffness:o,restDelta:h,restSpeed:u}))};return P(0),{calculatedDuration:null,next:t=>{let e=!1;return w||void 0!==T||(e=!0,x(t),P(t)),void 0!==T&&t>=T?w.next(t-T):(!e&&x(t),d)}}}function De(t,e,{clamp:n=!0,ease:i,mixer:s}={}){const o=t.length;if(e.length,1===o)return()=>e[0];if(2===o&&e[0]===e[1])return()=>e[1];const r=t[0]===t[1];t[0]>t[o-1]&&(t=[...t].reverse(),e=[...e].reverse());const a=function(t,e,n){const i=[],s=n||P.mix||ee,o=t.length-1;for(let n=0;n{if(r&&n1)for(;ih(w(t[0],t[o-1],e)):h}function Ce(t){const e=[0];return function(t,e){const n=t[t.length-1];for(let i=1;i<=e;i++){const s=C(0,e,i);t.push(Xt(n,1,s))}}(e,t.length-1),e}function ke({duration:t=300,keyframes:e,times:n,ease:i="easeInOut"}){const s=(t=>Array.isArray(t)&&"number"!=typeof t[0])(i)?i.map(_):_(i),o={done:!1,value:e[0]},r=function(t,e){return t.map(t=>t*e)}(n&&n.length===e.length?n:Ce(e),t),a=De(r,e,{ease:Array.isArray(s)?s:(l=e,h=s,l.map(()=>h||q).splice(0,l.length-1))});var l,h;return{calculatedDuration:t,next:e=>(o.value=a(e),o.done=e>=t,o)}}Ve.applyToOptions=t=>{const e=function(t,e=100,n){const i=n({...t,keyframes:[0,e]}),s=Math.min(oe(i),se);return{type:"keyframes",ease:t=>i.next(s*t).value/e,duration:L(s)}}(t,100,Ve);return t.ease=e.ease,t.duration=R(e.duration),t.type="keyframes",t};const Re=t=>null!==t;function Le(t,{repeat:e,repeatType:n="loop"},i,s=1){const o=t.filter(Re),r=s<0||e&&"loop"!==n&&e%2==1?0:o.length-1;return r&&void 0!==i?i:o[r]}const je={decay:Me,inertia:Me,tween:ke,keyframes:ke,spring:Ve};function Be(t){"string"==typeof t.type&&(t.type=je[t.type])}class Fe{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Oe=t=>t/100;class Ie extends Fe{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==at.now()&&this.tick(at.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;Be(t);const{type:e=ke,repeat:n=0,repeatDelay:i=0,repeatType:s,velocity:o=0}=t;let{keyframes:r}=t;const a=e||ke;a!==ke&&"number"!=typeof r[0]&&(this.mixKeyframes=D(Oe,ee(r[0],r[1])),r=[0,100]);const l=a({...t,keyframes:r});"mirror"===s&&(this.mirroredGenerator=a({...t,keyframes:[...r].reverse(),velocity:-o})),null===l.calculatedDuration&&(l.calculatedDuration=oe(l));const{calculatedDuration:h}=l;this.calculatedDuration=h,this.resolvedDuration=h+i,this.totalDuration=this.resolvedDuration*(n+1)-i,this.generator=l}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,e=!1){const{generator:n,totalDuration:i,mixKeyframes:s,mirroredGenerator:o,resolvedDuration:r,calculatedDuration:a}=this;if(null===this.startTime)return n.next(0);const{delay:l=0,keyframes:h,repeat:u,repeatType:c,repeatDelay:d,type:p,onUpdate:m,finalKeyframe:f}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-i/this.speed,this.startTime)),e?this.currentTime=t:this.updateTime(t);const y=this.currentTime-l*(this.playbackSpeed>=0?1:-1),g=this.playbackSpeed>=0?y<0:y>i;this.currentTime=Math.max(y,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=i);let v=this.currentTime,x=n;if(u){const t=Math.min(this.currentTime,i)/r;let e=Math.floor(t),n=t%1;!n&&t>=1&&(n=1),1===n&&e--,e=Math.min(e,u+1);Boolean(e%2)&&("reverse"===c?(n=1-n,d&&(n-=d/r)):"mirror"===c&&(x=o)),v=w(0,1,n)*r}const T=g?{done:!1,value:h[0]}:x.next(v);s&&(T.value=s(T.value));let{done:P}=T;g||null===a||(P=this.playbackSpeed>=0?this.currentTime>=i:this.currentTime<=0);const S=null===this.holdTime&&("finished"===this.state||"running"===this.state&&P);return S&&p!==Me&&(T.value=Le(h,this.options,f,this.speed)),m&&m(T.value),S&&this.finish(),T}then(t,e){return this.finished.then(t,e)}get duration(){return L(this.calculatedDuration)}get time(){return L(this.currentTime)}set time(t){t=R(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(at.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=L(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=ne,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const n=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=n):null!==this.holdTime?this.startTime=n-this.holdTime:this.startTime||(this.startTime=e??n),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(at.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}const Ue=t=>180*t/Math.PI,Ne=t=>{const e=Ue(Math.atan2(t[1],t[0]));return $e(e)},We={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:Ne,rotateZ:Ne,skewX:t=>Ue(Math.atan(t[1])),skewY:t=>Ue(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},$e=t=>((t%=360)<0&&(t+=360),t),Ye=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),Xe=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),Ke={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:Ye,scaleY:Xe,scale:t=>(Ye(t)+Xe(t))/2,rotateX:t=>$e(Ue(Math.atan2(t[6],t[5]))),rotateY:t=>$e(Ue(Math.atan2(-t[2],t[0]))),rotateZ:Ne,rotate:Ne,skewX:t=>Ue(Math.atan(t[4])),skewY:t=>Ue(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function ze(t){return t.includes("scale")?1:0}function He(t,e){if(!t||"none"===t)return ze(e);const n=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let i,s;if(n)i=Ke,s=n;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);i=We,s=e}if(!s)return ze(e);const o=i[e],r=s[1].split(",").map(qe);return"function"==typeof o?o(r):r[o]}function qe(t){return parseFloat(t.trim())}const Ge=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],Ze=(()=>new Set(Ge))(),_e=t=>t===pt||t===Vt,Je=new Set(["x","y","z"]),Qe=Ge.filter(t=>!Je.has(t));const tn={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>He(e,"x"),y:(t,{transform:e})=>He(e,"y")};tn.translateX=tn.x,tn.translateY=tn.y;const en=new Set;let nn=!1,sn=!1,on=!1;function rn(){if(sn){const t=Array.from(en).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return Qe.forEach(n=>{const i=t.getValue(n);void 0!==i&&(e.push([n,i.get()]),i.set(n.startsWith("scale")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{t.getValue(e)?.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}sn=!1,nn=!1,en.forEach(t=>t.complete(on)),en.clear()}function an(){en.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(sn=!0)})}class ln{constructor(t,e,n,i,s,o=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=i,this.element=s,this.isAsync=o}scheduleResolve(){this.state="scheduled",this.isAsync?(en.add(this),nn||(nn=!0,et.read(an),et.resolveKeyframes(rn))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:i}=this;if(null===t[0]){const s=i?.get(),o=t[t.length-1];if(void 0!==s)t[0]=s;else if(n&&e){const i=n.readValue(e,o);null!=i&&(t[0]=i)}void 0===t[0]&&(t[0]=o),i&&void 0===s&&i.set(t[0])}!function(t){for(let e=1;evoid 0!==window.ScrollTimeline),un={};function cn(t,e){const n=E(t);return()=>un[e]??n()}const dn=cn(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),pn=([t,e,n,i])=>`cubic-bezier(${t}, ${e}, ${n}, ${i})`,mn={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:pn([0,.65,.55,1]),circOut:pn([.55,0,1,.45]),backIn:pn([.31,.01,.66,-.59]),backOut:pn([.33,1.53,.69,.99])};function fn(t,e){return t?"function"==typeof t?dn()?ie(t,e):"ease-out":G(t)?pn(t):Array.isArray(t)?t.map(t=>fn(t,e)||mn.easeOut):mn[t]:void 0}function yn(t,e,n,{delay:i=0,duration:s=300,repeat:o=0,repeatType:r="loop",ease:a="easeOut",times:l}={},h=void 0){const u={[e]:n};l&&(u.offset=l);const c=fn(a,s);Array.isArray(c)&&(u.easing=c);const d={delay:i,duration:s,easing:Array.isArray(c)?"linear":c,fill:"both",iterations:o+1,direction:"reverse"===r?"alternate":"normal"};h&&(d.pseudoElement=h);return t.animate(u,d)}function gn(t){return"function"==typeof t&&"applyToOptions"in t}class vn extends Fe{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:n,keyframes:i,pseudoElement:s,allowFlatten:o=!1,finalKeyframe:r,onComplete:a}=t;this.isPseudoElement=Boolean(s),this.allowFlatten=o,this.options=t,t.type;const l=function({type:t,...e}){return gn(t)&&dn()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=yn(e,n,i,l,s),!1===l.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!s){const t=Le(i,this.options,r,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,n){(t=>t.startsWith("--"))(e)?t.style.setProperty(e,n):t.style[e]=n}(e,n,t),this.animation.cancel()}a?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return L(Number(t))}get time(){return L(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=R(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&hn()?(this.animation.timeline=t,V):e(this)}}const xn={anticipate:$,backInOut:W,circInOut:K};function Tn(t){"string"==typeof t.ease&&t.ease in xn&&(t.ease=xn[t.ease])}class wn extends vn{constructor(t){Tn(t),Be(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:n,onComplete:i,element:s,...o}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const r=new Ie({...o,autoplay:!1}),a=R(this.finishedTime??this.time);e.setWithVelocity(r.sample(a-10).value,r.sample(a).value,10),r.stop()}}const Pn=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!Wt.test(t)&&"0"!==t||t.startsWith("url(")));function Sn(t){return b(t)&&"offsetHeight"in t}const bn=new Set(["opacity","clipPath","filter","transform"]),An=E(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));class En extends Fe{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:i=0,repeatDelay:s=0,repeatType:o="loop",keyframes:r,name:a,motionValue:l,element:h,...u}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=at.now();const c={autoplay:t,delay:e,type:n,repeat:i,repeatDelay:s,repeatType:o,name:a,motionValue:l,element:h,...u},d=h?.KeyframeResolver||ln;this.keyframeResolver=new d(r,(t,e,n)=>this.onKeyframesResolved(t,e,c,!n),a,l,h),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,e,n,i){this.keyframeResolver=void 0;const{name:s,type:o,velocity:r,delay:a,isHandoff:l,onUpdate:h}=n;this.resolvedAt=at.now(),function(t,e,n,i){const s=t[0];if(null===s)return!1;if("display"===e||"visibility"===e)return!0;const o=t[t.length-1],r=Pn(s,e),a=Pn(o,e);return!(!r||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:e,...n,keyframes:t},c=!l&&function(t){const{motionValue:e,name:n,repeatDelay:i,repeatType:s,damping:o,type:r}=t;if(!Sn(e?.owner?.current))return!1;const{onUpdate:a,transformTemplate:l}=e.owner.getProps();return An()&&n&&bn.has(n)&&("transform"!==n||!l)&&!a&&!i&&"mirror"!==s&&0!==o&&"inertia"!==r}(u)?new wn({...u,element:u.motionValue.owner.current}):new Ie(u);c.finished.then(()=>this.notifyFinished()).catch(V),this.pendingTimeline&&(this.stopTimeline=c.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=c}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),on=!0,an(),rn(),on=!1),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}}const Vn=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function Mn(t,e,n=1){const[i,s]=function(t){const e=Vn.exec(t);if(!e)return[,];const[,n,i,s]=e;return[`--${n??i}`,s]}(t);if(!i)return;const o=window.getComputedStyle(e).getPropertyValue(i);if(o){const t=o.trim();return S(t)?parseFloat(t):t}return ct(s)?Mn(s,e,n+1):s}function Dn(t,e){return t?.[e]??t?.default??t}const Cn=new Set(["width","height","top","left","right","bottom",...Ge]),kn=t=>e=>e.test(t),Rn=[pt,Vt,Et,At,Dt,Mt,{test:t=>"auto"===t,parse:t=>t}],Ln=t=>Rn.find(kn(t));function jn(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||A(t))}const Bn=new Set(["brightness","contrast","saturate","opacity"]);function Fn(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[i]=n.match(gt)||[];if(!i)return t;const s=n.replace(i,"");let o=Bn.has(e)?1:0;return i!==n&&(o*=100),e+"("+o+s+")"}const On=/\b([a-z-]*)\(.*?\)/gu,In={...Wt,getAnimatableNone:t=>{const e=t.match(On);return e?e.map(Fn).join(" "):t}},Un={...pt,transform:Math.round},Nn={borderWidth:Vt,borderTopWidth:Vt,borderRightWidth:Vt,borderBottomWidth:Vt,borderLeftWidth:Vt,borderRadius:Vt,radius:Vt,borderTopLeftRadius:Vt,borderTopRightRadius:Vt,borderBottomRightRadius:Vt,borderBottomLeftRadius:Vt,width:Vt,maxWidth:Vt,height:Vt,maxHeight:Vt,top:Vt,right:Vt,bottom:Vt,left:Vt,padding:Vt,paddingTop:Vt,paddingRight:Vt,paddingBottom:Vt,paddingLeft:Vt,margin:Vt,marginTop:Vt,marginRight:Vt,marginBottom:Vt,marginLeft:Vt,backgroundPositionX:Vt,backgroundPositionY:Vt,...{rotate:At,rotateX:At,rotateY:At,rotateZ:At,scale:ft,scaleX:ft,scaleY:ft,scaleZ:ft,skew:At,skewX:At,skewY:At,distance:Vt,translateX:Vt,translateY:Vt,translateZ:Vt,x:Vt,y:Vt,z:Vt,perspective:Vt,transformPerspective:Vt,opacity:mt,originX:Ct,originY:Ct,originZ:Vt},zIndex:Un,fillOpacity:mt,strokeOpacity:mt,numOctaves:Un},Wn={...Nn,color:Rt,backgroundColor:Rt,outlineColor:Rt,fill:Rt,stroke:Rt,borderColor:Rt,borderTopColor:Rt,borderRightColor:Rt,borderBottomColor:Rt,borderLeftColor:Rt,filter:In,WebkitFilter:In},$n=t=>Wn[t];function Yn(t,e){let n=$n(t);return n!==In&&(n=Wt),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const Xn=new Set(["auto","none","0"]);class Kn extends ln{constructor(t,e,n,i,s){super(t,e,n,i,s,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{t.getValue(e).set(n)}),this.resolveNoneKeyframes()}}const zn=(t,e)=>e&&"number"==typeof t?e.transform(t):t;class Hn{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const n=at.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=at.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new k);const n=this.events[t].add(e);return"change"===t?()=>{n(),et.read(()=>{this.events.change.getSize()||this.stop()})}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=at.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return j(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function qn(t,e){return new Hn(t,e)}const{schedule:Gn,cancel:Zn}=tt(queueMicrotask,!1),_n={x:!1,y:!1};function Jn(){return _n.x||_n.y}function Qn(t,e){const n=function(t,e,n){if(t instanceof EventTarget)return[t];if("string"==typeof t){let i=document;e&&(i=e.current);const s=n?.[t]??i.querySelectorAll(t);return s?Array.from(s):[]}return Array.from(t)}(t),i=new AbortController;return[n,{passive:!0,...e,signal:i.signal},()=>i.abort()]}function ti(t){return!("touch"===t.pointerType||Jn())}const ei=(t,e)=>!!e&&(t===e||ei(t,e.parentElement)),ni=t=>"mouse"===t.pointerType?"number"!=typeof t.button||t.button<=0:!1!==t.isPrimary,ii=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);const si=new WeakSet;function oi(t){return e=>{"Enter"===e.key&&t(e)}}function ri(t,e){t.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}function ai(t){return ni(t)&&!Jn()}function li(t,e,n={}){const[i,s,o]=Qn(t,n),r=t=>{const i=t.currentTarget;if(!ai(t))return;si.add(i);const o=e(i,t),r=(t,e)=>{window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",l),si.has(i)&&si.delete(i),ai(t)&&"function"==typeof o&&o(t,{success:e})},a=t=>{r(t,i===window||i===document||n.useGlobalTarget||ei(i,t.target))},l=t=>{r(t,!1)};window.addEventListener("pointerup",a,s),window.addEventListener("pointercancel",l,s)};return i.forEach(t=>{var e;(n.useGlobalTarget?window:t).addEventListener("pointerdown",r,s),Sn(t)&&(t.addEventListener("focus",t=>((t,e)=>{const n=t.currentTarget;if(!n)return;const i=oi(()=>{if(si.has(n))return;ri(n,"down");const t=oi(()=>{ri(n,"up")});n.addEventListener("keyup",t,e),n.addEventListener("blur",()=>ri(n,"cancel"),e)});n.addEventListener("keydown",i,e),n.addEventListener("blur",()=>n.removeEventListener("keydown",i),e)})(t,s)),e=t,ii.has(e.tagName)||-1!==e.tabIndex||t.hasAttribute("tabindex")||(t.tabIndex=0))}),o}function hi(t){return b(t)&&"ownerSVGElement"in t}const ui=t=>Boolean(t&&t.getVelocity),ci=[...Rn,Rt,Wt],di=t=>Array.isArray(t);function pi(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,qn(n))}function mi(t){return di(t)?t[t.length-1]||0:t}function fi(t,e){const n=t.getValue("willChange");if(i=n,Boolean(ui(i)&&i.add))return n.add(e);if(!n&&P.WillChange){const n=new P.WillChange("auto");t.addValue("willChange",n),n.add(e)}var i}const yi=t=>t.replace(/([a-z])([A-Z])/gu,"$1-$2").toLowerCase(),gi="data-"+yi("framerAppearId");function vi(t){return t.props[gi]}const xi=t=>null!==t;const Ti={type:"spring",stiffness:500,damping:25,restSpeed:10},wi={type:"keyframes",duration:.8},Pi={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},Si=(t,{keyframes:e})=>e.length>2?wi:Ze.has(t)?t.startsWith("scale")?{type:"spring",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:Ti:Pi;const bi=(t,e,n,i={},s,o)=>r=>{const a=Dn(i,t)||{},l=a.delay||i.delay||0;let{elapsed:h=0}=i;h-=R(l);const u={keyframes:Array.isArray(n)?n:[null,n],ease:"easeOut",velocity:e.getVelocity(),...a,delay:-h,onUpdate:t=>{e.set(t),a.onUpdate&&a.onUpdate(t)},onComplete:()=>{r(),a.onComplete&&a.onComplete()},name:t,motionValue:e,element:o?void 0:s};(function({when:t,delay:e,delayChildren:n,staggerChildren:i,staggerDirection:s,repeat:o,repeatType:r,repeatDelay:a,from:l,elapsed:h,...u}){return!!Object.keys(u).length})(a)||Object.assign(u,Si(t,u)),u.duration&&(u.duration=R(u.duration)),u.repeatDelay&&(u.repeatDelay=R(u.repeatDelay)),void 0!==u.from&&(u.keyframes[0]=u.from);let c=!1;if((!1===u.type||0===u.duration&&!u.repeatDelay)&&(u.duration=0,0===u.delay&&(c=!0)),(P.instantAnimations||P.skipAnimations)&&(c=!0,u.duration=0,u.delay=0),u.allowFlatten=!a.type&&!a.ease,c&&!o&&void 0!==e.get()){const t=function(t,{repeat:e,repeatType:n="loop"},i){const s=t.filter(xi),o=e&&"loop"!==n&&e%2==1?0:s.length-1;return o&&void 0!==i?i:s[o]}(u.keyframes,a);if(void 0!==t)return void et.update(()=>{u.onUpdate(t),u.onComplete()})}return a.isSync?new Ie(u):new En(u)};function Ai({protectedKeys:t,needsAnimating:e},n){const i=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,i}function Ei(t,e,{delay:n=0,transitionOverride:i,type:s}={}){let{transition:o=t.getDefaultTransition(),transitionEnd:r,...a}=e;i&&(o=i);const l=[],h=s&&t.animationState&&t.animationState.getState()[s];for(const e in a){const i=t.getValue(e,t.latestValues[e]??null),s=a[e];if(void 0===s||h&&Ai(h,e))continue;const r={delay:n,...Dn(o||{},e)},u=i.get();if(void 0!==u&&!i.isAnimating&&!Array.isArray(s)&&s===u&&!r.velocity)continue;let c=!1;if(window.MotionHandoffAnimation){const n=vi(t);if(n){const t=window.MotionHandoffAnimation(n,e,et);null!==t&&(r.startTime=t,c=!0)}}fi(t,e),i.start(bi(e,i,s,t.shouldReduceMotion&&Cn.has(e)?{type:!1}:r,t,c));const d=i.animation;d&&l.push(d)}return r&&Promise.all(l).then(()=>{et.update(()=>{r&&function(t,e){const n=v(t,e);let{transitionEnd:i={},transition:s={},...o}=n||{};o={...o,...i};for(const e in o)pi(t,e,mi(o[e]))}(t,r)})}),l}function Vi(t,e,n={}){const i=v(t,e,"exit"===n.type?t.presenceContext?.custom:void 0);let{transition:s=t.getDefaultTransition()||{}}=i||{};n.transitionOverride&&(s=n.transitionOverride);const o=i?()=>Promise.all(Ei(t,i,n)):()=>Promise.resolve(),r=t.variantChildren&&t.variantChildren.size?(i=0)=>{const{delayChildren:o=0,staggerChildren:r,staggerDirection:a}=s;return function(t,e,n=0,i=0,s=0,o=1,r){const a=[],l=t.variantChildren.size,h=(l-1)*s,u="function"==typeof i,c=u?t=>i(t,l):1===o?(t=0)=>t*s:(t=0)=>h-t*s;return Array.from(t.variantChildren).sort(Mi).forEach((t,s)=>{t.notify("AnimationStart",e),a.push(Vi(t,e,{...r,delay:n+(u?0:i)+c(s)}).then(()=>t.notify("AnimationComplete",e)))}),Promise.all(a)}(t,e,i,o,r,a,n)}:()=>Promise.resolve(),{when:a}=s;if(a){const[t,e]="beforeChildren"===a?[o,r]:[r,o];return t().then(()=>e())}return Promise.all([o(),r(n.delay)])}function Mi(t,e){return t.sortNodePosition(e)}function Di(t,e){if(!Array.isArray(e))return!1;const n=e.length;if(n!==t.length)return!1;for(let i=0;iPromise.all(e.map(({animation:e,options:n})=>function(t,e,n={}){let i;if(t.notify("AnimationStart",e),Array.isArray(e)){const s=e.map(e=>Vi(t,e,n));i=Promise.all(s)}else if("string"==typeof e)i=Vi(t,e,n);else{const s="function"==typeof e?v(t,e,n.custom):e;i=Promise.all(Ei(t,s,n))}return i.then(()=>{t.notify("AnimationComplete",e)})}(t,e,n)))}function Ii(t){let e=Oi(t),n=Wi(),i=!0;const s=e=>(n,i)=>{const s=v(t,i,"exit"===e?t.presenceContext?.custom:void 0);if(s){const{transition:t,transitionEnd:e,...i}=s;n={...n,...i,...e}}return n};function o(o){const{props:r}=t,a=ji(t.parent)||{},l=[],h=new Set;let u={},c=1/0;for(let e=0;ec&&y,w=!1;const P=Array.isArray(m)?m:[m];let S=P.reduce(s(d),{});!1===g&&(S={});const{prevResolvedValues:b={}}=p,A={...b,...S},E=e=>{T=!0,h.has(e)&&(w=!0,h.delete(e)),p.needsAnimating[e]=!0;const n=t.getValue(e);n&&(n.liveStyle=!1)};for(const t in A){const e=S[t],n=b[t];if(u.hasOwnProperty(t))continue;let i=!1;i=di(e)&&di(n)?!Di(e,n):e!==n,i?null!=e?E(t):h.add(t):void 0!==e&&h.has(t)?E(t):p.protectedKeys[t]=!0}p.prevProp=m,p.prevResolvedValues=S,p.isActive&&(u={...u,...S}),i&&t.blockInitialAnimation&&(T=!1);T&&(!(v&&x)||w)&&l.push(...P.map(t=>({animation:t,options:{type:d}})))}if(h.size){const e={};if("boolean"!=typeof r.initial){const n=v(t,Array.isArray(r.initial)?r.initial[0]:r.initial);n&&n.transition&&(e.transition=n.transition)}h.forEach(n=>{const i=t.getBaseTarget(n),s=t.getValue(n);s&&(s.liveStyle=!0),e[n]=i??null}),l.push({animation:e})}let d=Boolean(l.length);return!i||!1!==r.initial&&r.initial!==r.animate||t.manuallyAnimateOnMount||(d=!1),i=!1,d?e(l):Promise.resolve()}return{animateChanges:o,setActive:function(e,i){if(n[e].isActive===i)return Promise.resolve();t.variantChildren?.forEach(t=>t.animationState?.setActive(e,i)),n[e].isActive=i;const s=o(e);for(const t in n)n[t].protectedKeys={};return s},setAnimateFunction:function(n){e=n(t)},getState:()=>n,reset:()=>{n=Wi(),i=!0}}}function Ui(t,e){return"string"==typeof e?e!==t:!!Array.isArray(e)&&!Di(e,t)}function Ni(t=!1){return{isActive:t,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function Wi(){return{animate:Ni(!0),whileInView:Ni(),whileHover:Ni(),whileTap:Ni(),whileDrag:Ni(),whileFocus:Ni(),exit:Ni()}}class $i{constructor(t){this.isMounted=!1,this.node=t}update(){}}let Yi=0;const Xi={animation:{Feature:class extends $i{constructor(t){super(t),t.animationState||(t.animationState=Ii(t))}updateAnimationControlsSubscription(){const{animate:t}=this.node.getProps();f(t)&&(this.unmountControls=t.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:t}=this.node.getProps(),{animate:e}=this.node.prevProps||{};t!==e&&this.updateAnimationControlsSubscription()}unmount(){this.node.animationState.reset(),this.unmountControls?.()}}},exit:{Feature:class extends $i{constructor(){super(...arguments),this.id=Yi++}update(){if(!this.node.presenceContext)return;const{isPresent:t,onExitComplete:e}=this.node.presenceContext,{isPresent:n}=this.node.prevPresenceContext||{};if(!this.node.animationState||t===n)return;const i=this.node.animationState.setActive("exit",!t);e&&!t&&i.then(()=>{e(this.id)})}mount(){const{register:t,onExitComplete:e}=this.node.presenceContext||{};e&&e(this.id),t&&(this.unmount=t(this.id))}unmount(){}}}};function Ki(t,e,n,i={passive:!0}){return t.addEventListener(e,n,i),()=>t.removeEventListener(e,n)}function zi(t){return{point:{x:t.pageX,y:t.pageY}}}function Hi(t,e,n,i){return Ki(t,e,(t=>e=>ni(e)&&t(e,zi(e)))(n),i)}function qi({top:t,left:e,right:n,bottom:i}){return{x:{min:e,max:n},y:{min:t,max:i}}}function Gi(t){return t.max-t.min}function Zi(t,e,n,i=.5){t.origin=i,t.originPoint=Xt(e.min,e.max,t.origin),t.scale=Gi(n)/Gi(e),t.translate=Xt(n.min,n.max,t.origin)-t.originPoint,(t.scale>=.9999&&t.scale<=1.0001||isNaN(t.scale))&&(t.scale=1),(t.translate>=-.01&&t.translate<=.01||isNaN(t.translate))&&(t.translate=0)}function _i(t,e,n,i){Zi(t.x,e.x,n.x,i?i.originX:void 0),Zi(t.y,e.y,n.y,i?i.originY:void 0)}function Ji(t,e,n){t.min=n.min+e.min,t.max=t.min+Gi(e)}function Qi(t,e,n){t.min=e.min-n.min,t.max=t.min+Gi(e)}function ts(t,e,n){Qi(t.x,e.x,n.x),Qi(t.y,e.y,n.y)}const es=()=>({x:{min:0,max:0},y:{min:0,max:0}});function ns(t){return[t("x"),t("y")]}function is(t){return void 0===t||1===t}function ss({scale:t,scaleX:e,scaleY:n}){return!is(t)||!is(e)||!is(n)}function os(t){return ss(t)||rs(t)||t.z||t.rotate||t.rotateX||t.rotateY||t.skewX||t.skewY}function rs(t){return as(t.x)||as(t.y)}function as(t){return t&&"0%"!==t}function ls(t,e,n){return n+e*(t-n)}function hs(t,e,n,i,s){return void 0!==s&&(t=ls(t,s,i)),ls(t,n,i)+e}function us(t,e=0,n=1,i,s){t.min=hs(t.min,e,n,i,s),t.max=hs(t.max,e,n,i,s)}function cs(t,{x:e,y:n}){us(t.x,e.translate,e.scale,e.originPoint),us(t.y,n.translate,n.scale,n.originPoint)}const ds=.999999999999,ps=1.0000000000001;function ms(t,e){t.min=t.min+e,t.max=t.max+e}function fs(t,e,n,i,s=.5){us(t,e,n,Xt(t.min,t.max,s),i)}function ys(t,e){fs(t.x,e.x,e.scaleX,e.scale,e.originX),fs(t.y,e.y,e.scaleY,e.scale,e.originY)}function gs(t,e){return qi(function(t,e){if(!e)return t;const n=e({x:t.left,y:t.top}),i=e({x:t.right,y:t.bottom});return{top:n.y,left:n.x,bottom:i.y,right:i.x}}(t.getBoundingClientRect(),e))}const vs=({current:t})=>t?t.ownerDocument.defaultView:null;function xs(t){return t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"current")}const Ts=(t,e)=>Math.abs(t-e);class ws{constructor(t,e,{transformPagePoint:n,contextWindow:i=window,dragSnapToOrigin:s=!1,distanceThreshold:o=3}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.updatePoint=()=>{if(!this.lastMoveEvent||!this.lastMoveEventInfo)return;const t=bs(this.lastMoveEventInfo,this.history),e=null!==this.startEvent,n=function(t,e){const n=Ts(t.x,e.x),i=Ts(t.y,e.y);return Math.sqrt(n**2+i**2)}(t.offset,{x:0,y:0})>=this.distanceThreshold;if(!e&&!n)return;const{point:i}=t,{timestamp:s}=it;this.history.push({...i,timestamp:s});const{onStart:o,onMove:r}=this.handlers;e||(o&&o(this.lastMoveEvent,t),this.startEvent=this.lastMoveEvent),r&&r(this.lastMoveEvent,t)},this.handlePointerMove=(t,e)=>{this.lastMoveEvent=t,this.lastMoveEventInfo=Ps(e,this.transformPagePoint),et.update(this.updatePoint,!0)},this.handlePointerUp=(t,e)=>{this.end();const{onEnd:n,onSessionEnd:i,resumeAnimation:s}=this.handlers;if(this.dragSnapToOrigin&&s&&s(),!this.lastMoveEvent||!this.lastMoveEventInfo)return;const o=bs("pointercancel"===t.type?this.lastMoveEventInfo:Ps(e,this.transformPagePoint),this.history);this.startEvent&&n&&n(t,o),i&&i(t,o)},!ni(t))return;this.dragSnapToOrigin=s,this.handlers=e,this.transformPagePoint=n,this.distanceThreshold=o,this.contextWindow=i||window;const r=Ps(zi(t),this.transformPagePoint),{point:a}=r,{timestamp:l}=it;this.history=[{...a,timestamp:l}];const{onSessionStart:h}=e;h&&h(t,bs(r,this.history)),this.removeListeners=D(Hi(this.contextWindow,"pointermove",this.handlePointerMove),Hi(this.contextWindow,"pointerup",this.handlePointerUp),Hi(this.contextWindow,"pointercancel",this.handlePointerUp))}updateHandlers(t){this.handlers=t}end(){this.removeListeners&&this.removeListeners(),nt(this.updatePoint)}}function Ps(t,e){return e?{point:e(t.point)}:t}function Ss(t,e){return{x:t.x-e.x,y:t.y-e.y}}function bs({point:t},e){return{point:t,delta:Ss(t,Es(e)),offset:Ss(t,As(e)),velocity:Vs(e,.1)}}function As(t){return t[0]}function Es(t){return t[t.length-1]}function Vs(t,e){if(t.length<2)return{x:0,y:0};let n=t.length-1,i=null;const s=Es(t);for(;n>=0&&(i=t[n],!(s.timestamp-i.timestamp>R(e)));)n--;if(!i)return{x:0,y:0};const o=L(s.timestamp-i.timestamp);if(0===o)return{x:0,y:0};const r={x:(s.x-i.x)/o,y:(s.y-i.y)/o};return r.x===1/0&&(r.x=0),r.y===1/0&&(r.y=0),r}function Ms(t,e,n){return{min:void 0!==e?t.min+e:void 0,max:void 0!==n?t.max+n-(t.max-t.min):void 0}}function Ds(t,e){let n=e.min-t.min,i=e.max-t.max;return e.max-e.min{const{dragSnapToOrigin:n}=this.getProps();n?this.pauseAnimation():this.stopAnimation(),e&&this.snapToCursor(zi(t).point)},onStart:(t,e)=>{const{drag:n,dragPropagation:i,onDragStart:s}=this.getProps();if(n&&!i&&(this.openDragLock&&this.openDragLock(),this.openDragLock="x"===(o=n)||"y"===o?_n[o]?null:(_n[o]=!0,()=>{_n[o]=!1}):_n.x||_n.y?null:(_n.x=_n.y=!0,()=>{_n.x=_n.y=!1}),!this.openDragLock))return;var o;this.latestPointerEvent=t,this.latestPanInfo=e,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),ns(t=>{let e=this.getAxisMotionValue(t).get()||0;if(Et.test(e)){const{projection:n}=this.visualElement;if(n&&n.layout){const i=n.layout.layoutBox[t];if(i){e=Gi(i)*(parseFloat(e)/100)}}}this.originPoint[t]=e}),s&&et.postRender(()=>s(t,e)),fi(this.visualElement,"transform");const{animationState:r}=this.visualElement;r&&r.setActive("whileDrag",!0)},onMove:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e;const{dragPropagation:n,dragDirectionLock:i,onDirectionLock:s,onDrag:o}=this.getProps();if(!n&&!this.openDragLock)return;const{offset:r}=e;if(i&&null===this.currentDirection)return this.currentDirection=function(t,e=10){let n=null;Math.abs(t.y)>e?n="y":Math.abs(t.x)>e&&(n="x");return n}(r),void(null!==this.currentDirection&&s&&s(this.currentDirection));this.updateAxis("x",e.point,r),this.updateAxis("y",e.point,r),this.visualElement.render(),o&&o(t,e)},onSessionEnd:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e,this.stop(t,e),this.latestPointerEvent=null,this.latestPanInfo=null},resumeAnimation:()=>ns(t=>"paused"===this.getAnimationState(t)&&this.getAxisMotionValue(t).animation?.play())},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:s,distanceThreshold:n,contextWindow:vs(this.visualElement)})}stop(t,e){const n=t||this.latestPointerEvent,i=e||this.latestPanInfo,s=this.isDragging;if(this.cancel(),!s||!i||!n)return;const{velocity:o}=i;this.startAnimation(o);const{onDragEnd:r}=this.getProps();r&&et.postRender(()=>r(n,i))}cancel(){this.isDragging=!1;const{projection:t,animationState:e}=this.visualElement;t&&(t.isAnimationBlocked=!1),this.panSession&&this.panSession.end(),this.panSession=void 0;const{dragPropagation:n}=this.getProps();!n&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),e&&e.setActive("whileDrag",!1)}updateAxis(t,e,n){const{drag:i}=this.getProps();if(!n||!Bs(t,i,this.currentDirection))return;const s=this.getAxisMotionValue(t);let o=this.originPoint[t]+n[t];this.constraints&&this.constraints[t]&&(o=function(t,{min:e,max:n},i){return void 0!==e&&tn&&(t=i?Xt(n,t,i.max):Math.min(t,n)),t}(o,this.constraints[t],this.elastic[t])),s.set(o)}resolveConstraints(){const{dragConstraints:t,dragElastic:e}=this.getProps(),n=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):this.visualElement.projection?.layout,i=this.constraints;t&&xs(t)?this.constraints||(this.constraints=this.resolveRefConstraints()):this.constraints=!(!t||!n)&&function(t,{top:e,left:n,bottom:i,right:s}){return{x:Ms(t.x,n,s),y:Ms(t.y,e,i)}}(n.layoutBox,t),this.elastic=function(t=Cs){return!1===t?t=0:!0===t&&(t=Cs),{x:ks(t,"left","right"),y:ks(t,"top","bottom")}}(e),i!==this.constraints&&n&&this.constraints&&!this.hasMutatedConstraints&&ns(t=>{!1!==this.constraints&&this.getAxisMotionValue(t)&&(this.constraints[t]=function(t,e){const n={};return void 0!==e.min&&(n.min=e.min-t.min),void 0!==e.max&&(n.max=e.max-t.min),n}(n.layoutBox[t],this.constraints[t]))})}resolveRefConstraints(){const{dragConstraints:t,onMeasureDragConstraints:e}=this.getProps();if(!t||!xs(t))return!1;const n=t.current,{projection:i}=this.visualElement;if(!i||!i.layout)return!1;const s=function(t,e,n){const i=gs(t,n),{scroll:s}=e;return s&&(ms(i.x,s.offset.x),ms(i.y,s.offset.y)),i}(n,i.root,this.visualElement.getTransformPagePoint());let o=function(t,e){return{x:Ds(t.x,e.x),y:Ds(t.y,e.y)}}(i.layout.layoutBox,s);if(e){const t=e(function({x:t,y:e}){return{top:e.min,right:t.max,bottom:e.max,left:t.min}}(o));this.hasMutatedConstraints=!!t,t&&(o=qi(t))}return o}startAnimation(t){const{drag:e,dragMomentum:n,dragElastic:i,dragTransition:s,dragSnapToOrigin:o,onDragTransitionEnd:r}=this.getProps(),a=this.constraints||{},l=ns(r=>{if(!Bs(r,e,this.currentDirection))return;let l=a&&a[r]||{};o&&(l={min:0,max:0});const h=i?200:1e6,u=i?40:1e7,c={type:"inertia",velocity:n?t[r]:0,bounceStiffness:h,bounceDamping:u,timeConstant:750,restDelta:1,restSpeed:10,...s,...l};return this.startAxisValueAnimation(r,c)});return Promise.all(l).then(r)}startAxisValueAnimation(t,e){const n=this.getAxisMotionValue(t);return fi(this.visualElement,t),n.start(bi(t,n,0,e,this.visualElement,!1))}stopAnimation(){ns(t=>this.getAxisMotionValue(t).stop())}pauseAnimation(){ns(t=>this.getAxisMotionValue(t).animation?.pause())}getAnimationState(t){return this.getAxisMotionValue(t).animation?.state}getAxisMotionValue(t){const e=`_drag${t.toUpperCase()}`,n=this.visualElement.getProps(),i=n[e];return i||this.visualElement.getValue(t,(n.initial?n.initial[t]:void 0)||0)}snapToCursor(t){ns(e=>{const{drag:n}=this.getProps();if(!Bs(e,n,this.currentDirection))return;const{projection:i}=this.visualElement,s=this.getAxisMotionValue(e);if(i&&i.layout){const{min:n,max:o}=i.layout.layoutBox[e];s.set(t[e]-Xt(n,o,.5))}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:t,dragConstraints:e}=this.getProps(),{projection:n}=this.visualElement;if(!xs(e)||!n||!this.constraints)return;this.stopAnimation();const i={x:0,y:0};ns(t=>{const e=this.getAxisMotionValue(t);if(e&&!1!==this.constraints){const n=e.get();i[t]=function(t,e){let n=.5;const i=Gi(t),s=Gi(e);return s>i?n=C(e.min,e.max-i,t.min):i>s&&(n=C(t.min,t.max-s,e.min)),w(0,1,n)}({min:n,max:n},this.constraints[t])}});const{transformTemplate:s}=this.visualElement.getProps();this.visualElement.current.style.transform=s?s({},""):"none",n.root&&n.root.updateScroll(),n.updateLayout(),this.resolveConstraints(),ns(e=>{if(!Bs(e,t,null))return;const n=this.getAxisMotionValue(e),{min:s,max:o}=this.constraints[e];n.set(Xt(s,o,i[e]))})}addListeners(){if(!this.visualElement.current)return;Ls.set(this.visualElement,this);const t=Hi(this.visualElement.current,"pointerdown",t=>{const{drag:e,dragListener:n=!0}=this.getProps();e&&n&&this.start(t)}),e=()=>{const{dragConstraints:t}=this.getProps();xs(t)&&t.current&&(this.constraints=this.resolveRefConstraints())},{projection:n}=this.visualElement,i=n.addEventListener("measure",e);n&&!n.layout&&(n.root&&n.root.updateScroll(),n.updateLayout()),et.read(e);const s=Ki(window,"resize",()=>this.scalePositionWithinConstraints()),o=n.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e})=>{this.isDragging&&e&&(ns(e=>{const n=this.getAxisMotionValue(e);n&&(this.originPoint[e]+=t[e].translate,n.set(n.get()+t[e].translate))}),this.visualElement.render())});return()=>{s(),t(),i(),o&&o()}}getProps(){const t=this.visualElement.getProps(),{drag:e=!1,dragDirectionLock:n=!1,dragPropagation:i=!1,dragConstraints:s=!1,dragElastic:o=Cs,dragMomentum:r=!0}=t;return{...t,drag:e,dragDirectionLock:n,dragPropagation:i,dragConstraints:s,dragElastic:o,dragMomentum:r}}}function Bs(t,e,n){return!(!0!==e&&e!==t||null!==n&&n!==t)}const Fs=t=>(e,n)=>{t&&et.postRender(()=>t(e,n))};const Os=(t,e)=>t.depth-e.depth;class Is{constructor(){this.children=[],this.isDirty=!1}add(t){x(this.children,t),this.isDirty=!0}remove(t){T(this.children,t),this.isDirty=!0}forEach(t){this.isDirty&&this.children.sort(Os),this.isDirty=!1,this.children.forEach(t)}}function Us(t){return ui(t)?t.get():t}const Ns=["TopLeft","TopRight","BottomLeft","BottomRight"],Ws=Ns.length,$s=t=>"string"==typeof t?parseFloat(t):t,Ys=t=>"number"==typeof t||Vt.test(t);function Xs(t,e){return void 0!==t[e]?t[e]:t.borderRadius}const Ks=Hs(0,.5,X),zs=Hs(.5,.95,V);function Hs(t,e,n){return i=>ie?1:n(C(t,e,i))}function qs(t,e){t.min=e.min,t.max=e.max}function Gs(t,e){qs(t.x,e.x),qs(t.y,e.y)}function Zs(t,e){t.translate=e.translate,t.scale=e.scale,t.originPoint=e.originPoint,t.origin=e.origin}function _s(t,e,n,i,s){return t=ls(t-=e,1/n,i),void 0!==s&&(t=ls(t,1/s,i)),t}function Js(t,e,[n,i,s],o,r){!function(t,e=0,n=1,i=.5,s,o=t,r=t){Et.test(e)&&(e=parseFloat(e),e=Xt(r.min,r.max,e/100)-r.min);if("number"!=typeof e)return;let a=Xt(o.min,o.max,i);t===o&&(a-=e),t.min=_s(t.min,e,n,a,s),t.max=_s(t.max,e,n,a,s)}(t,e[n],e[i],e[s],e.scale,o,r)}const Qs=["x","scaleX","originX"],to=["y","scaleY","originY"];function eo(t,e,n,i){Js(t.x,e,Qs,n?n.x:void 0,i?i.x:void 0),Js(t.y,e,to,n?n.y:void 0,i?i.y:void 0)}function no(t){return 0===t.translate&&1===t.scale}function io(t){return no(t.x)&&no(t.y)}function so(t,e){return t.min===e.min&&t.max===e.max}function oo(t,e){return Math.round(t.min)===Math.round(e.min)&&Math.round(t.max)===Math.round(e.max)}function ro(t,e){return oo(t.x,e.x)&&oo(t.y,e.y)}function ao(t){return Gi(t.x)/Gi(t.y)}function lo(t,e){return t.translate===e.translate&&t.scale===e.scale&&t.originPoint===e.originPoint}class ho{constructor(){this.members=[]}add(t){x(this.members,t),t.scheduleRender()}remove(t){if(T(this.members,t),t===this.prevLead&&(this.prevLead=void 0),t===this.lead){const t=this.members[this.members.length-1];t&&this.promote(t)}}relegate(t){const e=this.members.findIndex(e=>t===e);if(0===e)return!1;let n;for(let t=e;t>=0;t--){const e=this.members[t];if(!1!==e.isPresent){n=e;break}}return!!n&&(this.promote(n),!0)}promote(t,e){const n=this.lead;if(t!==n&&(this.prevLead=n,this.lead=t,t.show(),n)){n.instance&&n.scheduleRender(),t.scheduleRender(),t.resumeFrom=n,e&&(t.resumeFrom.preserveOpacity=!0),n.snapshot&&(t.snapshot=n.snapshot,t.snapshot.latestValues=n.animationValues||n.latestValues),t.root&&t.root.isUpdating&&(t.isLayoutDirty=!0);const{crossfade:i}=t.options;!1===i&&n.hide()}}exitAnimationComplete(){this.members.forEach(t=>{const{options:e,resumingFrom:n}=t;e.onExitComplete&&e.onExitComplete(),n&&n.options.onExitComplete&&n.options.onExitComplete()})}scheduleRender(){this.members.forEach(t=>{t.instance&&t.scheduleRender(!1)})}removeLeadSnapshot(){this.lead&&this.lead.snapshot&&(this.lead.snapshot=void 0)}}const uo={};const co={hasAnimatedSinceResize:!0,hasEverUpdated:!1},po=["","X","Y","Z"];let mo=0;function fo(t,e,n,i){const{latestValues:s}=e;s[t]&&(n[t]=s[t],e.setStaticValue(t,0),i&&(i[t]=0))}function yo(t){if(t.hasCheckedOptimisedAppear=!0,t.root===t)return;const{visualElement:e}=t.options;if(!e)return;const n=vi(e);if(window.MotionHasOptimisedAnimation(n,"transform")){const{layout:e,layoutId:i}=t.options;window.MotionCancelOptimisedAnimation(n,"transform",et,!(e||i))}const{parent:i}=t;i&&!i.hasCheckedOptimisedAppear&&yo(i)}function go({attachResizeListener:t,defaultParent:e,measureScroll:n,checkIsScrollRoot:i,resetTransform:s}){return class{constructor(t={},n=e?.()){this.id=mo++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,this.nodes.forEach(To),this.nodes.forEach(Vo),this.nodes.forEach(Mo),this.nodes.forEach(wo)},this.resolvedRelativeTargetAt=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=t,this.root=n?n.root||n:this,this.path=n?[...n.path,n]:[],this.parent=n,this.depth=n?n.depth+1:0;for(let t=0;tthis.root.updateBlockedByResize=!1;et.read(()=>{i=window.innerWidth}),t(e,()=>{const t=window.innerWidth;t!==i&&(i=t,this.root.updateBlockedByResize=!0,n&&n(),n=function(t,e){const n=at.now(),i=({timestamp:s})=>{const o=s-n;o>=e&&(nt(i),t(o-e))};return et.setup(i,!0),()=>nt(i)}(s,250),co.hasAnimatedSinceResize&&(co.hasAnimatedSinceResize=!1,this.nodes.forEach(Eo)))})}i&&this.root.registerSharedNode(i,this),!1!==this.options.animate&&o&&(i||s)&&this.addEventListener("didUpdate",({delta:t,hasLayoutChanged:e,hasRelativeLayoutChanged:n,layout:i})=>{if(this.isTreeAnimationBlocked())return this.target=void 0,void(this.relativeTarget=void 0);const s=this.options.transition||o.getDefaultTransition()||jo,{onLayoutAnimationStart:r,onLayoutAnimationComplete:a}=o.getProps(),l=!this.targetLayout||!ro(this.targetLayout,i),h=!e&&n;if(this.options.layoutRoot||this.resumeFrom||h||e&&(l||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const e={...Dn(s,"layout"),onPlay:r,onComplete:a};(o.shouldReduceMotion||this.options.layoutRoot)&&(e.delay=0,e.type=!1),this.startAnimation(e),this.setAnimationOrigin(t,h)}else e||Eo(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=i})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const t=this.getStack();t&&t.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),nt(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(Do),this.animationId++)}getTransformTemplate(){const{visualElement:t}=this.options;return t&&t.getProps().transformTemplate}willUpdate(t=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked())return void(this.options.onExitComplete&&this.options.onExitComplete());if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&yo(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let t=0;t{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){!this.snapshot&&this.instance&&(this.snapshot=this.measure(),!this.snapshot||Gi(this.snapshot.measuredBox.x)||Gi(this.snapshot.measuredBox.y)||(this.snapshot=void 0))}updateLayout(){if(!this.instance)return;if(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead()||this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let t=0;tds&&(e.x=1),e.yds&&(e.y=1)}(this.layoutCorrected,this.treeScale,this.path,e),!t.layout||t.target||1===this.treeScale.x&&1===this.treeScale.y||(t.target=t.layout.layoutBox,t.targetWithTransforms={x:{min:0,max:0},y:{min:0,max:0}});const{target:a}=t;a?(this.projectionDelta&&this.prevProjectionDelta?(Zs(this.prevProjectionDelta.x,this.projectionDelta.x),Zs(this.prevProjectionDelta.y,this.projectionDelta.y)):this.createProjectionDeltas(),_i(this.projectionDelta,this.layoutCorrected,a,this.latestValues),this.treeScale.x===o&&this.treeScale.y===r&&lo(this.projectionDelta.x,this.prevProjectionDelta.x)&&lo(this.projectionDelta.y,this.prevProjectionDelta.y)||(this.hasProjected=!0,this.scheduleRender(),this.notifyListeners("projectionUpdate",a))):this.prevProjectionDelta&&(this.createProjectionDeltas(),this.scheduleRender())}hide(){this.isVisible=!1}show(){this.isVisible=!0}scheduleRender(t=!0){if(this.options.visualElement?.scheduleRender(),t){const t=this.getStack();t&&t.scheduleRender()}this.resumingFrom&&!this.resumingFrom.instance&&(this.resumingFrom=void 0)}createProjectionDeltas(){this.prevProjectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDeltaWithTransform={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}}}setAnimationOrigin(t,e=!1){const n=this.snapshot,i=n?n.latestValues:{},s={...this.latestValues},o={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};this.relativeParent&&this.relativeParent.options.layoutRoot||(this.relativeTarget=this.relativeTargetOrigin=void 0),this.attemptToResolveRelativeTarget=!e;const r={x:{min:0,max:0},y:{min:0,max:0}},a=(n?n.source:void 0)!==(this.layout?this.layout.source:void 0),l=this.getStack(),h=!l||l.members.length<=1,u=Boolean(a&&!h&&!0===this.options.crossfade&&!this.path.some(Lo));let c;this.animationProgress=0,this.mixTargetDelta=e=>{const n=e/1e3;var l,d,p,m,f,y;ko(o.x,t.x,n),ko(o.y,t.y,n),this.setTargetDelta(o),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(ts(r,this.layout.layoutBox,this.relativeParent.layout.layoutBox),p=this.relativeTarget,m=this.relativeTargetOrigin,f=r,y=n,Ro(p.x,m.x,f.x,y),Ro(p.y,m.y,f.y,y),c&&(l=this.relativeTarget,d=c,so(l.x,d.x)&&so(l.y,d.y))&&(this.isProjectionDirty=!1),c||(c={x:{min:0,max:0},y:{min:0,max:0}}),Gs(c,this.relativeTarget)),a&&(this.animationValues=s,function(t,e,n,i,s,o){s?(t.opacity=Xt(0,n.opacity??1,Ks(i)),t.opacityExit=Xt(e.opacity??1,0,zs(i))):o&&(t.opacity=Xt(e.opacity??1,n.opacity??1,i));for(let s=0;s{co.hasAnimatedSinceResize=!0,this.motionValue||(this.motionValue=qn(0)),this.currentAnimation=function(t,e,n){const i=ui(t)?t:qn(t);return i.start(bi("",i,e,n)),i.animation}(this.motionValue,[0,1e3],{...t,velocity:0,isSync:!0,onUpdate:e=>{this.mixTargetDelta(e),t.onUpdate&&t.onUpdate(e)},onStop:()=>{},onComplete:()=>{t.onComplete&&t.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const t=this.getStack();t&&t.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners("animationComplete")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(1e3),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const t=this.getLead();let{targetWithTransforms:e,target:n,layout:i,latestValues:s}=t;if(e&&n&&i){if(this!==t&&this.layout&&i&&Io(this.options.animationType,this.layout.layoutBox,i.layoutBox)){n=this.target||{x:{min:0,max:0},y:{min:0,max:0}};const e=Gi(this.layout.layoutBox.x);n.x.min=t.target.x.min,n.x.max=n.x.min+e;const i=Gi(this.layout.layoutBox.y);n.y.min=t.target.y.min,n.y.max=n.y.min+i}Gs(e,n),ys(e,s),_i(this.projectionDeltaWithTransform,this.layoutCorrected,e,s)}}registerSharedNode(t,e){this.sharedNodes.has(t)||this.sharedNodes.set(t,new ho);this.sharedNodes.get(t).add(e);const n=e.options.initialPromotionConfig;e.promote({transition:n?n.transition:void 0,preserveFollowOpacity:n&&n.shouldPreserveFollowOpacity?n.shouldPreserveFollowOpacity(e):void 0})}isLead(){const t=this.getStack();return!t||t.lead===this}getLead(){const{layoutId:t}=this.options;return t&&this.getStack()?.lead||this}getPrevLead(){const{layoutId:t}=this.options;return t?this.getStack()?.prevLead:void 0}getStack(){const{layoutId:t}=this.options;if(t)return this.root.sharedNodes.get(t)}promote({needsReset:t,transition:e,preserveFollowOpacity:n}={}){const i=this.getStack();i&&i.promote(this,n),t&&(this.projectionDelta=void 0,this.needsReset=!0),e&&this.setOptions({transition:e})}relegate(){const t=this.getStack();return!!t&&t.relegate(this)}resetSkewAndRotation(){const{visualElement:t}=this.options;if(!t)return;let e=!1;const{latestValues:n}=t;if((n.z||n.rotate||n.rotateX||n.rotateY||n.rotateZ||n.skewX||n.skewY)&&(e=!0),!e)return;const i={};n.z&&fo("z",t,i,this.animationValues);for(let e=0;et.currentAnimation?.stop()),this.root.nodes.forEach(So),this.root.sharedNodes.clear()}}}function vo(t){t.updateLayout()}function xo(t){const e=t.resumeFrom?.snapshot||t.snapshot;if(t.isLead()&&t.layout&&e&&t.hasListeners("didUpdate")){const{layoutBox:n,measuredBox:i}=t.layout,{animationType:s}=t.options,o=e.source!==t.layout.source;"size"===s?ns(t=>{const i=o?e.measuredBox[t]:e.layoutBox[t],s=Gi(i);i.min=n[t].min,i.max=i.min+s}):Io(s,e.layoutBox,n)&&ns(i=>{const s=o?e.measuredBox[i]:e.layoutBox[i],r=Gi(n[i]);s.max=s.min+r,t.relativeTarget&&!t.currentAnimation&&(t.isProjectionDirty=!0,t.relativeTarget[i].max=t.relativeTarget[i].min+r)});const r={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};_i(r,n,e.layoutBox);const a={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};o?_i(a,t.applyTransform(i,!0),e.measuredBox):_i(a,n,e.layoutBox);const l=!io(r);let h=!1;if(!t.resumeFrom){const i=t.getClosestProjectingParent();if(i&&!i.resumeFrom){const{snapshot:s,layout:o}=i;if(s&&o){const r={x:{min:0,max:0},y:{min:0,max:0}};ts(r,e.layoutBox,s.layoutBox);const a={x:{min:0,max:0},y:{min:0,max:0}};ts(a,n,o.layoutBox),ro(r,a)||(h=!0),i.options.layoutRoot&&(t.relativeTarget=a,t.relativeTargetOrigin=r,t.relativeParent=i)}}}t.notifyListeners("didUpdate",{layout:n,snapshot:e,delta:a,layoutDelta:r,hasLayoutChanged:l,hasRelativeLayoutChanged:h})}else if(t.isLead()){const{onExitComplete:e}=t.options;e&&e()}t.options.transition=void 0}function To(t){t.parent&&(t.isProjecting()||(t.isProjectionDirty=t.parent.isProjectionDirty),t.isSharedProjectionDirty||(t.isSharedProjectionDirty=Boolean(t.isProjectionDirty||t.parent.isProjectionDirty||t.parent.isSharedProjectionDirty)),t.isTransformDirty||(t.isTransformDirty=t.parent.isTransformDirty))}function wo(t){t.isProjectionDirty=t.isSharedProjectionDirty=t.isTransformDirty=!1}function Po(t){t.clearSnapshot()}function So(t){t.clearMeasurements()}function bo(t){t.isLayoutDirty=!1}function Ao(t){const{visualElement:e}=t.options;e&&e.getProps().onBeforeLayoutMeasure&&e.notify("BeforeLayoutMeasure"),t.resetTransform()}function Eo(t){t.finishAnimation(),t.targetDelta=t.relativeTarget=t.target=void 0,t.isProjectionDirty=!0}function Vo(t){t.resolveTargetDelta()}function Mo(t){t.calcProjection()}function Do(t){t.resetSkewAndRotation()}function Co(t){t.removeLeadSnapshot()}function ko(t,e,n){t.translate=Xt(e.translate,0,n),t.scale=Xt(e.scale,1,n),t.origin=e.origin,t.originPoint=e.originPoint}function Ro(t,e,n,i){t.min=Xt(e.min,n.min,i),t.max=Xt(e.max,n.max,i)}function Lo(t){return t.animationValues&&void 0!==t.animationValues.opacityExit}const jo={duration:.45,ease:[.4,0,.1,1]},Bo=t=>"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(t),Fo=Bo("applewebkit/")&&!Bo("chrome/")?Math.round:V;function Oo(t){t.min=Fo(t.min),t.max=Fo(t.max)}function Io(t,e,n){return"position"===t||"preserve-aspect"===t&&(i=ao(e),s=ao(n),o=.2,!(Math.abs(i-s)<=o));var i,s,o}function Uo(t){return t!==t.root&&t.scroll?.wasRoot}const No=go({attachResizeListener:(t,e)=>Ki(t,"resize",e),measureScroll:()=>({x:document.documentElement.scrollLeft||document.body.scrollLeft,y:document.documentElement.scrollTop||document.body.scrollTop}),checkIsScrollRoot:()=>!0}),Wo={current:void 0},$o=go({measureScroll:t=>({x:t.scrollLeft,y:t.scrollTop}),defaultParent:()=>{if(!Wo.current){const t=new No({});t.mount(window),t.setOptions({layoutScroll:!0}),Wo.current=t}return Wo.current},resetTransform:(t,e)=>{t.style.transform=void 0!==e?e:"none"},checkIsScrollRoot:t=>Boolean("fixed"===window.getComputedStyle(t).position)});function Yo(t,e){return e.max===e.min?0:t/(e.max-e.min)*100}const Xo={correct:(t,e)=>{if(!e.target)return t;if("string"==typeof t){if(!Vt.test(t))return t;t=parseFloat(t)}return`${Yo(t,e.target.x)}% ${Yo(t,e.target.y)}%`}},Ko={correct:(t,{treeScale:e,projectionDelta:n})=>{const i=t,s=Wt.parse(t);if(s.length>5)return i;const o=Wt.createTransformer(t),r="number"!=typeof s[0]?1:0,a=n.x.scale*e.x,l=n.y.scale*e.y;s[0+r]/=a,s[1+r]/=l;const h=Xt(a,l,.5);return"number"==typeof s[2+r]&&(s[2+r]/=h),"number"==typeof s[3+r]&&(s[3+r]/=h),o(s)}},zo={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]},Ho={};for(const t in zo)Ho[t]={isEnabled:e=>zo[t].some(t=>!!e[t])};const qo="undefined"!=typeof window,Go={current:null},Zo={current:!1};const _o=new WeakMap;function Jo(t){return f(t.animate)||Ri.some(e=>Ci(t[e]))}function Qo(t){return Boolean(Jo(t)||t.variants)}const tr=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];class er{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:i,blockInitialAnimation:s,visualState:o},r={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=ln,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=at.now();this.renderScheduledAtthis.bindToMotionValue(e,t)),Zo.current||function(){if(Zo.current=!0,qo)if(window.matchMedia){const t=window.matchMedia("(prefers-reduced-motion)"),e=()=>Go.current=t.matches;t.addEventListener("change",e),e()}else Go.current=!1}(),this.shouldReduceMotion="never"!==this.reducedMotionConfig&&("always"===this.reducedMotionConfig||Go.current),this.parent&&this.parent.children.add(this),this.update(this.props,this.presenceContext)}unmount(){this.projection&&this.projection.unmount(),nt(this.notifyUpdate),nt(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),this.parent&&this.parent.children.delete(this);for(const t in this.events)this.events[t].clear();for(const t in this.features){const e=this.features[t];e&&(e.unmount(),e.isMounted=!1)}this.current=null}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=Ze.has(t);n&&this.onBindTransform&&this.onBindTransform();const i=e.on("change",e=>{this.latestValues[t]=e,this.props.onUpdate&&et.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0)}),s=e.on("renderRequest",this.scheduleRender);let o;window.MotionCheckAppearSync&&(o=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{i(),s(),o&&o(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t="animation";for(t in Ho){const e=Ho[t];if(!e)continue;const{isEnabled:n,Feature:i}=e;if(!this.features[t]&&i&&n(this.props)&&(this.features[t]=new i(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let e=0;ee.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=qn(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){let n=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];var i;return null!=n&&("string"==typeof n&&(S(n)||A(n))?n=parseFloat(n):(i=n,!ci.find(kn(i))&&Wt.test(e)&&(n=Yn(t,e))),this.setBaseTarget(t,ui(n)?n.get():n)),ui(n)?n.get():n}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){const{initial:e}=this.props;let n;if("string"==typeof e||"object"==typeof e){const i=g(this.props,e,this.presenceContext?.custom);i&&(n=i[t])}if(e&&void 0!==n)return n;const i=this.getBaseTargetFromProps(this.props,t);return void 0===i||ui(i)?void 0!==this.initialValues[t]&&void 0===n?void 0:this.baseTarget[t]:i}on(t,e){return this.events[t]||(this.events[t]=new k),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}}class nr extends er{constructor(){super(...arguments),this.KeyframeResolver=Kn}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;ui(t)&&(this.childSubscription=t.on("change",t=>{this.current&&(this.current.textContent=`${t}`)}))}}const ir={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},sr=Ge.length;function or(t,e,n){const{style:i,vars:s,transformOrigin:o}=t;let r=!1,a=!1;for(const t in e){const n=e[t];if(Ze.has(t))r=!0;else if(ht(t))s[t]=n;else{const e=zn(n,Nn[t]);t.startsWith("origin")?(a=!0,o[t]=e):i[t]=e}}if(e.transform||(r||n?i.transform=function(t,e,n){let i="",s=!0;for(let o=0;o{const{transform:n="none"}=getComputedStyle(t);return He(n,e)})(t,e);{const i=(n=t,window.getComputedStyle(n)),s=(ht(e)?i.getPropertyValue(e):i[e])||0;return"string"==typeof s?s.trim():s}var n}measureInstanceViewportBox(t,{transformPagePoint:e}){return gs(t,e)}build(t,e,n){or(t,e,n.transformTemplate)}scrapeMotionValuesFromProps(t,e,n){return lr(t,e,n)}}const ur=n(null);const cr=n({}),dr=n({});let pr=!1;class mr extends a{componentDidMount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:n,layoutId:i}=this.props,{projection:s}=t;!function(t){for(const e in t)uo[e]=t[e],ht(e)&&(uo[e].isCSSVariable=!0)}(yr),s&&(e.group&&e.group.add(s),n&&n.register&&i&&n.register(s),pr&&s.root.didUpdate(),s.addEventListener("animationComplete",()=>{this.safeToRemove()}),s.setOptions({...s.options,onExitComplete:()=>this.safeToRemove()})),co.hasEverUpdated=!0}getSnapshotBeforeUpdate(t){const{layoutDependency:e,visualElement:n,drag:i,isPresent:s}=this.props,{projection:o}=n;return o?(o.isPresent=s,pr=!0,i||t.layoutDependency!==e||void 0===e||t.isPresent!==s?o.willUpdate():this.safeToRemove(),t.isPresent!==s&&(s?o.promote():o.relegate()||et.postRender(()=>{const t=o.getStack();t&&t.members.length||this.safeToRemove()})),null):null}componentDidUpdate(){const{projection:t}=this.props.visualElement;t&&(t.root.didUpdate(),Gn.postRender(()=>{!t.currentAnimation&&t.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:n}=this.props,{projection:i}=t;i&&(i.scheduleCheckAfterUnmount(),e&&e.group&&e.group.remove(i),n&&n.deregister&&n.deregister(i))}safeToRemove(){const{safeToRemove:t}=this.props;t&&t()}render(){return null}}function fr(e){const[n,a]=function(t=!0){const e=i(ur);if(null===e)return[!0,null];const{isPresent:n,onExitComplete:a,register:l}=e,h=s();o(()=>{if(t)return l(h)},[t]);const u=r(()=>t&&a&&a(h),[h,a,t]);return!n&&a?[!1,u]:[!0]}(),l=i(cr);return t(mr,{...e,layoutGroup:l,switchLayoutGroup:i(dr),isPresent:n,safeToRemove:a})}const yr={borderRadius:{...Xo,applyTo:["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"]},borderTopLeftRadius:Xo,borderTopRightRadius:Xo,borderBottomLeftRadius:Xo,borderBottomRightRadius:Xo,boxShadow:Ko},gr={pan:{Feature:class extends $i{constructor(){super(...arguments),this.removePointerDownListener=V}onPointerDown(t){this.session=new ws(t,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:vs(this.node)})}createPanHandlers(){const{onPanSessionStart:t,onPanStart:e,onPan:n,onPanEnd:i}=this.node.getProps();return{onSessionStart:Fs(t),onStart:Fs(e),onMove:n,onEnd:(t,e)=>{delete this.session,i&&et.postRender(()=>i(t,e))}}}mount(){this.removePointerDownListener=Hi(this.node.current,"pointerdown",t=>this.onPointerDown(t))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}},drag:{Feature:class extends $i{constructor(t){super(t),this.removeGroupControls=V,this.removeListeners=V,this.controls=new js(t)}mount(){const{dragControls:t}=this.node.getProps();t&&(this.removeGroupControls=t.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||V}unmount(){this.removeGroupControls(),this.removeListeners()}},ProjectionNode:$o,MeasureLayout:fr}};function vr(t,e,n){const{props:i}=t;t.animationState&&i.whileHover&&t.animationState.setActive("whileHover","Start"===n);const s=i["onHover"+n];s&&et.postRender(()=>s(e,zi(e)))}function xr(t,e,n){const{props:i}=t;if(t.current instanceof HTMLButtonElement&&t.current.disabled)return;t.animationState&&i.whileTap&&t.animationState.setActive("whileTap","Start"===n);const s=i["onTap"+("End"===n?"":n)];s&&et.postRender(()=>s(e,zi(e)))}const Tr=new WeakMap,wr=new WeakMap,Pr=t=>{const e=Tr.get(t.target);e&&e(t)},Sr=t=>{t.forEach(Pr)};function br(t,e,n){const i=function({root:t,...e}){const n=t||document;wr.has(n)||wr.set(n,{});const i=wr.get(n),s=JSON.stringify(e);return i[s]||(i[s]=new IntersectionObserver(Sr,{root:t,...e})),i[s]}(e);return Tr.set(t,n),i.observe(t),()=>{Tr.delete(t),i.unobserve(t)}}const Ar={some:0,all:1};const Er={inView:{Feature:class extends $i{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){this.unmount();const{viewport:t={}}=this.node.getProps(),{root:e,margin:n,amount:i="some",once:s}=t,o={root:e?e.current:void 0,rootMargin:n,threshold:"number"==typeof i?i:Ar[i]};return br(this.node.current,o,t=>{const{isIntersecting:e}=t;if(this.isInView===e)return;if(this.isInView=e,s&&!e&&this.hasEnteredView)return;e&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",e);const{onViewportEnter:n,onViewportLeave:i}=this.node.getProps(),o=e?n:i;o&&o(t)})}mount(){this.startObserver()}update(){if("undefined"==typeof IntersectionObserver)return;const{props:t,prevProps:e}=this.node;["amount","margin","root"].some(function({viewport:t={}},{viewport:e={}}={}){return n=>t[n]!==e[n]}(t,e))&&this.startObserver()}unmount(){}}},tap:{Feature:class extends $i{mount(){const{current:t}=this.node;t&&(this.unmount=li(t,(t,e)=>(xr(this.node,e,"Start"),(t,{success:e})=>xr(this.node,t,e?"End":"Cancel")),{useGlobalTarget:this.node.props.globalTapTarget}))}unmount(){}}},focus:{Feature:class extends $i{constructor(){super(...arguments),this.isActive=!1}onFocus(){let t=!1;try{t=this.node.current.matches(":focus-visible")}catch(e){t=!0}t&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){this.isActive&&this.node.animationState&&(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=D(Ki(this.node.current,"focus",()=>this.onFocus()),Ki(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}},hover:{Feature:class extends $i{mount(){const{current:t}=this.node;t&&(this.unmount=function(t,e,n={}){const[i,s,o]=Qn(t,n),r=t=>{if(!ti(t))return;const{target:n}=t,i=e(n,t);if("function"!=typeof i||!n)return;const o=t=>{ti(t)&&(i(t),n.removeEventListener("pointerleave",o))};n.addEventListener("pointerleave",o,s)};return i.forEach(t=>{t.addEventListener("pointerenter",r,s)}),o}(t,(t,e)=>(vr(this.node,e,"Start"),t=>vr(this.node,t,"End"))))}unmount(){}}}},Vr={layout:{ProjectionNode:$o,MeasureLayout:fr}},Mr=n({strict:!1}),Dr=n({transformPagePoint:t=>t,isStatic:!1,reducedMotion:"never"}),Cr=n({});function kr(t){const{initial:e,animate:n}=function(t,e){if(Jo(t)){const{initial:e,animate:n}=t;return{initial:!1===e||Ci(e)?e:void 0,animate:Ci(n)?n:void 0}}return!1!==t.inherit?e:{}}(t,i(Cr));return l(()=>({initial:e,animate:n}),[Rr(e),Rr(n)])}function Rr(t){return Array.isArray(t)?t.join(" "):t}const Lr=Symbol.for("motionComponentSymbol");function jr(t,e,n){return r(i=>{i&&t.onMount&&t.onMount(i),e&&(i?e.mount(i):e.unmount()),n&&("function"==typeof n?n(i):xs(n)&&(n.current=i))},[e])}const Br=qo?h:o;function Fr(t,e,n,s,r){const{visualElement:a}=i(Cr),l=i(Mr),h=i(ur),d=i(Dr).reducedMotion,p=u(null);s=s||l.renderer,!p.current&&s&&(p.current=s(t,{visualState:e,parent:a,props:n,presenceContext:h,blockInitialAnimation:!!h&&!1===h.initial,reducedMotionConfig:d}));const m=p.current,f=i(dr);!m||m.projection||!r||"html"!==m.type&&"svg"!==m.type||function(t,e,n,i){const{layoutId:s,layout:o,drag:r,dragConstraints:a,layoutScroll:l,layoutRoot:h,layoutCrossfade:u}=e;t.projection=new n(t.latestValues,e["data-framer-portal-id"]?void 0:Or(t.parent)),t.projection.setOptions({layoutId:s,layout:o,alwaysMeasureLayout:Boolean(r)||a&&xs(a),visualElement:t,animationType:"string"==typeof o?o:"both",initialPromotionConfig:i,crossfade:u,layoutScroll:l,layoutRoot:h})}(p.current,n,r,f);const y=u(!1);c(()=>{m&&y.current&&m.update(n,h)});const g=n[gi],v=u(Boolean(g)&&!window.MotionHandoffIsComplete?.(g)&&window.MotionHasOptimisedAnimation?.(g));return Br(()=>{m&&(y.current=!0,window.MotionIsMounted=!0,m.updateFeatures(),Gn.render(m.render),v.current&&m.animationState&&m.animationState.animateChanges())}),o(()=>{m&&(!v.current&&m.animationState&&m.animationState.animateChanges(),v.current&&(queueMicrotask(()=>{window.MotionHandoffMarkAsComplete?.(g)}),v.current=!1))}),m}function Or(t){if(t)return!1!==t.options.allowProjection?t.projection:Or(t.parent)}function Ir({preloadedFeatures:n,createVisualElement:s,useRender:o,useVisualState:r,Component:a}){function l(n,l){let h;const u={...i(Dr),...n,layoutId:Ur(n)},{isStatic:c}=u,d=kr(n),p=r(n,c);if(!c&&qo){i(Mr).strict;const t=function(t){const{drag:e,layout:n}=Ho;if(!e&&!n)return{};const i={...e,...n};return{MeasureLayout:e?.isEnabled(t)||n?.isEnabled(t)?i.MeasureLayout:void 0,ProjectionNode:i.ProjectionNode}}(u);h=t.MeasureLayout,d.visualElement=Fr(a,p,u,s,t.ProjectionNode)}return e(Cr.Provider,{value:d,children:[h&&d.visualElement?t(h,{visualElement:d.visualElement,...u}):null,o(a,n,jr(p,d.visualElement,l),p,c,d.visualElement)]})}n&&function(t){for(const e in t)Ho[e]={...Ho[e],...t[e]}}(n),l.displayName=`motion.${"string"==typeof a?a:`create(${a.displayName??a.name??""})`}`;const h=d(l);return h[Lr]=a,h}function Ur({layoutId:t}){const e=i(cr).id;return e&&void 0!==t?e+"-"+t:t}const Nr=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function Wr(t,e,n){for(const i in e)ui(e[i])||ar(i,n)||(t[i]=e[i])}function $r(t,e){const n={};return Wr(n,t.style||{},t),Object.assign(n,function({transformTemplate:t},e){return l(()=>{const n={style:{},transform:{},transformOrigin:{},vars:{}};return or(n,e,t),Object.assign({},n.vars,n.style)},[e])}(t,e)),n}function Yr(t,e){const n={},i=$r(t,e);return t.drag&&!1!==t.dragListener&&(n.draggable=!1,i.userSelect=i.WebkitUserSelect=i.WebkitTouchCallout="none",i.touchAction=!0===t.drag?"none":"pan-"+("x"===t.drag?"y":"x")),void 0===t.tabIndex&&(t.onTap||t.onTapStart||t.whileTap)&&(n.tabIndex=0),n.style=i,n}const Xr={offset:"stroke-dashoffset",array:"stroke-dasharray"},Kr={offset:"strokeDashoffset",array:"strokeDasharray"};function zr(t,{attrX:e,attrY:n,attrScale:i,pathLength:s,pathSpacing:o=1,pathOffset:r=0,...a},l,h,u){if(or(t,a,h),l)return void(t.style.viewBox&&(t.attrs.viewBox=t.style.viewBox));t.attrs=t.style,t.style={};const{attrs:c,style:d}=t;c.transform&&(d.transform=c.transform,delete c.transform),(d.transform||c.transformOrigin)&&(d.transformOrigin=c.transformOrigin??"50% 50%",delete c.transformOrigin),d.transform&&(d.transformBox=u?.transformBox??"fill-box",delete c.transformBox),void 0!==e&&(c.x=e),void 0!==n&&(c.y=n),void 0!==i&&(c.scale=i),void 0!==s&&function(t,e,n=1,i=0,s=!0){t.pathLength=1;const o=s?Xr:Kr;t[o.offset]=Vt.transform(-i);const r=Vt.transform(e),a=Vt.transform(n);t[o.array]=`${r} ${a}`}(c,s,o,r,!1)}const Hr=()=>({style:{},transform:{},transformOrigin:{},vars:{},attrs:{}}),qr=t=>"string"==typeof t&&"svg"===t.toLowerCase();function Gr(t,e,n,i){const s=l(()=>{const n={style:{},transform:{},transformOrigin:{},vars:{},attrs:{}};return zr(n,e,qr(i),t.transformTemplate,t.style),{...n.attrs,style:{...n.style}}},[e]);if(t.style){const e={};Wr(e,t.style,t),s.style={...e,...s.style}}return s}const Zr=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","ignoreStrict","viewport"]);function _r(t){return t.startsWith("while")||t.startsWith("drag")&&"draggable"!==t||t.startsWith("layout")||t.startsWith("onTap")||t.startsWith("onPan")||t.startsWith("onLayout")||Zr.has(t)}let Jr=t=>!_r(t);try{"function"==typeof(Qr=require("@emotion/is-prop-valid").default)&&(Jr=t=>t.startsWith("on")?!_r(t):Qr(t))}catch{}var Qr;const ta=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function ea(t){return"string"==typeof t&&!t.includes("-")&&!!(ta.indexOf(t)>-1||/[A-Z]/u.test(t))}function na(t=!1){return(e,n,i,{latestValues:s},o)=>{const r=(ea(e)?Gr:Yr)(n,s,o,e),a=function(t,e,n){const i={};for(const s in t)"values"===s&&"object"==typeof t.values||(Jr(s)||!0===n&&_r(s)||!e&&!_r(s)||t.draggable&&s.startsWith("onDrag"))&&(i[s]=t[s]);return i}(n,"string"==typeof e,t),h=e!==p?{...a,...r,ref:i}:{},{children:u}=n,c=l(()=>ui(u)?u.get():u,[u]);return m(e,{...h,children:c})}}const ia=t=>(e,n)=>{const s=i(Cr),o=i(ur),r=()=>function({scrapeMotionValuesFromProps:t,createRenderState:e},n,i,s){return{latestValues:sa(n,i,s,t),renderState:e()}}(t,e,s,o);return n?r():function(t){const e=u(null);return null===e.current&&(e.current=t()),e.current}(r)};function sa(t,e,n,i){const s={},o=i(t,{});for(const t in o)s[t]=Us(o[t]);let{initial:r,animate:a}=t;const l=Jo(t),h=Qo(t);e&&h&&!l&&!1!==t.inherit&&(void 0===r&&(r=e.initial),void 0===a&&(a=e.animate));let u=!!n&&!1===n.initial;u=u||!1===r;const c=u?a:r;if(c&&"boolean"!=typeof c&&!f(c)){const e=Array.isArray(c)?c:[c];for(let n=0;nea(t)?new ua(e):new hr(e,{allowProjection:t!==p}),da=la({...Xi,...Er,...gr,...Vr},ca)("div");export{da as MotionDiv}; diff --git a/node_modules/framer-motion/dist/size-rollup-scroll.js b/node_modules/framer-motion/dist/size-rollup-scroll.js new file mode 100644 index 00000000..fbdc24d5 --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-scroll.js @@ -0,0 +1 @@ +const e=(e,t,n)=>n>t?t:n(void 0===t&&(t=e()),t)}const s=e=>e,o=(e,t)=>n=>t(e(n)),i=(...e)=>e.reduce(o),a=(e,t,n)=>{const r=t-e;return 0===r?1:(n-e)/r};const c=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],u={value:null,addProjectionMetrics:null};function l(e,n){let r=!1,s=!0;const o={delta:0,timestamp:0,isProcessing:!1},i=()=>r=!0,a=c.reduce((e,t)=>(e[t]=function(e,t){let n=new Set,r=new Set,s=!1,o=!1;const i=new WeakSet;let a={delta:0,timestamp:0,isProcessing:!1},c=0;function l(t){i.has(t)&&(f.schedule(t),e()),c++,t(a)}const f={schedule:(e,t=!1,o=!1)=>{const a=o&&s?n:r;return t&&i.add(e),a.has(e)||a.add(e),e},cancel:e=>{r.delete(e),i.delete(e)},process:e=>{a=e,s?o=!0:(s=!0,[n,r]=[r,n],n.forEach(l),t&&u.value&&u.value.frameloop[t].push(c),c=0,n.clear(),s=!1,o&&(o=!1,f.process(e)))}};return f}(i,n?t:void 0),e),{}),{setup:l,read:f,resolveKeyframes:g,preUpdate:d,update:h,preRender:p,render:m,postRender:v}=a,y=()=>{const i=t.useManualTiming?o.timestamp:performance.now();r=!1,t.useManualTiming||(o.delta=s?1e3/60:Math.max(Math.min(i-o.timestamp,40),1)),o.timestamp=i,o.isProcessing=!0,l.process(o),f.process(o),g.process(o),d.process(o),h.process(o),p.process(o),m.process(o),v.process(o),o.isProcessing=!1,r&&n&&(s=!1,e(y))};return{schedule:c.reduce((t,n)=>{const i=a[n];return t[n]=(t,n=!1,a=!1)=>(r||(r=!0,s=!0,o.isProcessing||e(y)),i.schedule(t,n,a)),t},{}),cancel:e=>{for(let t=0;tt=>"string"==typeof t&&t.startsWith(e))("var(--"),m=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,v={test:e=>"number"==typeof e,parse:parseFloat,transform:e=>e},y={...v,transform:t=>e(0,1,t)},b=e=>Math.round(1e5*e)/1e5,w=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const x=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,E=(e,t)=>n=>Boolean("string"==typeof n&&x.test(n)&&n.startsWith(e)||t&&!function(e){return null==e}(n)&&Object.prototype.hasOwnProperty.call(n,t)),W=(e,t,n)=>r=>{if("string"!=typeof r)return r;const[s,o,i,a]=r.match(w);return{[e]:parseFloat(s),[t]:parseFloat(o),[n]:parseFloat(i),alpha:void 0!==a?parseFloat(a):1}},L={...v,transform:t=>Math.round((t=>e(0,255,t))(t))},M={test:E("rgb","red"),parse:W("red","green","blue"),transform:({red:e,green:t,blue:n,alpha:r=1})=>"rgba("+L.transform(e)+", "+L.transform(t)+", "+L.transform(n)+", "+b(y.transform(r))+")"};const A={test:E("#"),parse:function(e){let t="",n="",r="",s="";return e.length>5?(t=e.substring(1,3),n=e.substring(3,5),r=e.substring(5,7),s=e.substring(7,9)):(t=e.substring(1,2),n=e.substring(2,3),r=e.substring(3,4),s=e.substring(4,5),t+=t,n+=n,r+=r,s+=s),{red:parseInt(t,16),green:parseInt(n,16),blue:parseInt(r,16),alpha:s?parseInt(s,16)/255:1}},transform:M.transform},B=(e=>({test:t=>"string"==typeof t&&t.endsWith(e)&&1===t.split(" ").length,parse:parseFloat,transform:t=>`${t}${e}`}))("%"),S={test:E("hsl","hue"),parse:W("hue","saturation","lightness"),transform:({hue:e,saturation:t,lightness:n,alpha:r=1})=>"hsla("+Math.round(e)+", "+B.transform(b(t))+", "+B.transform(b(n))+", "+b(y.transform(r))+")"},O={test:e=>M.test(e)||A.test(e)||S.test(e),parse:e=>M.test(e)?M.parse(e):S.test(e)?S.parse(e):A.parse(e),transform:e=>"string"==typeof e?e:e.hasOwnProperty("red")?M.transform(e):S.transform(e),getAnimatableNone:e=>{const t=O.parse(e);return t.alpha=0,O.transform(t)}},T=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const z="number",H="color",F=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function N(e){const t=e.toString(),n=[],r={color:[],number:[],var:[]},s=[];let o=0;const i=t.replace(F,e=>(O.test(e)?(r.color.push(o),s.push(H),n.push(O.parse(e))):e.startsWith("var(")?(r.var.push(o),s.push("var"),n.push(e)):(r.number.push(o),s.push(z),n.push(parseFloat(e))),++o,"${}")).split("${}");return{values:n,split:i,indexes:r,types:s}}function P(e){return N(e).values}function $(e){const{split:t,types:n}=N(e),r=t.length;return e=>{let s="";for(let o=0;o"number"==typeof e?0:O.test(e)?O.getAnimatableNone(e):e;const k={test:function(e){return isNaN(e)&&"string"==typeof e&&(e.match(w)?.length||0)+(e.match(T)?.length||0)>0},parse:P,createTransformer:$,getAnimatableNone:function(e){const t=P(e);return $(e)(t.map(R))}};function j(e,t,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?e+6*(t-e)*n:n<.5?t:n<2/3?e+(t-e)*(2/3-n)*6:e}function q(e,t){return n=>n>0?t:e}const I=(e,t,n)=>e+(t-e)*n,U=(e,t,n)=>{const r=e*e,s=n*(t*t-r)+r;return s<0?0:Math.sqrt(s)},G=[A,M,S];function C(e){const t=(n=e,G.find(e=>e.test(n)));var n;if(!Boolean(t))return!1;let r=t.parse(e);return t===S&&(r=function({hue:e,saturation:t,lightness:n,alpha:r}){e/=360,n/=100;let s=0,o=0,i=0;if(t/=100){const r=n<.5?n*(1+t):n+t-n*t,a=2*n-r;s=j(a,r,e+1/3),o=j(a,r,e),i=j(a,r,e-1/3)}else s=o=i=n;return{red:Math.round(255*s),green:Math.round(255*o),blue:Math.round(255*i),alpha:r}}(r)),r}const K=(e,t)=>{const n=C(e),r=C(t);if(!n||!r)return q(e,t);const s={...n};return e=>(s.red=U(n.red,r.red,e),s.green=U(n.green,r.green,e),s.blue=U(n.blue,r.blue,e),s.alpha=I(n.alpha,r.alpha,e),M.transform(s))},V=new Set(["none","hidden"]);function D(e,t){return n=>I(e,t,n)}function J(e){return"number"==typeof e?D:"string"==typeof e?p(t=e)&&m.test(t.split("/*")[0].trim())?q:O.test(e)?K:Y:Array.isArray(e)?Q:"object"==typeof e?O.test(e)?K:X:q;var t}function Q(e,t){const n=[...e],r=n.length,s=e.map((e,n)=>J(e)(e,t[n]));return e=>{for(let t=0;t{for(const t in r)n[t]=r[t](e);return n}}const Y=(e,t)=>{const n=k.createTransformer(t),r=N(e),s=N(t);return r.indexes.var.length===s.indexes.var.length&&r.indexes.color.length===s.indexes.color.length&&r.indexes.number.length>=s.indexes.number.length?V.has(e)&&!s.values.length||V.has(t)&&!r.values.length?function(e,t){return V.has(e)?n=>n<=0?e:t:n=>n>=1?t:e}(e,t):i(Q(function(e,t){const n=[],r={color:0,var:0,number:0};for(let s=0;sr[0];if(2===l&&r[0]===r[1])return()=>r[1];const f=n[0]===n[1];n[0]>n[l-1]&&(n=[...n].reverse(),r=[...r].reverse());const g=function(e,n,r){const o=[],a=r||t.mix||Z,c=e.length-1;for(let t=0;t{if(f&&e1)for(;th(e(n[0],n[l-1],t)):h}function ee(e){const t=[0];return function(e,t){const n=e[e.length-1];for(let r=1;r<=t;r++){const s=a(0,t,r);e.push(I(n,1,s))}}(t,e.length-1),t}const te=r(()=>void 0!==window.ScrollTimeline);function ne(e){return n(e)&&"offsetHeight"in e}const re=new WeakMap;let se;const oe=(e,t,r)=>(s,o)=>{return o&&o[0]?o[0][e+"Size"]:n(i=s)&&"ownerSVGElement"in i&&"getBBox"in s?s.getBBox()[t]:s[r];var i},ie=oe("inline","width","offsetWidth"),ae=oe("block","height","offsetHeight");function ce({target:e,borderBoxSize:t}){re.get(e)?.forEach(n=>{n(e,{get width(){return ie(e,t)},get height(){return ae(e,t)}})})}function ue(e){e.forEach(ce)}function le(e,t){se||"undefined"!=typeof ResizeObserver&&(se=new ResizeObserver(ue));const n=function(e,t,n){if(e instanceof EventTarget)return[e];if("string"==typeof e){let r=document;t&&(r=t.current);const s=n?.[e]??r.querySelectorAll(e);return s?Array.from(s):[]}return Array.from(e)}(e);return n.forEach(e=>{let n=re.get(e);n||(n=new Set,re.set(e,n)),n.add(t),se?.observe(e)}),()=>{n.forEach(e=>{const n=re.get(e);n?.delete(t),n?.size||se?.unobserve(e)})}}const fe=new Set;let ge;function de(e){return fe.add(e),ge||(ge=()=>{const e={get width(){return window.innerWidth},get height(){return window.innerHeight}};fe.forEach(t=>t(e))},window.addEventListener("resize",ge)),()=>{fe.delete(e),fe.size||"function"!=typeof ge||(window.removeEventListener("resize",ge),ge=void 0)}}function he(e,t){let n;const r=()=>{const{currentTime:r}=t,s=(null===r?0:r.value)/100;n!==s&&e(s),n=s};return f.preUpdate(r,!0),()=>g(r)}const pe={x:{length:"Width",position:"Left"},y:{length:"Height",position:"Top"}};function me(e,t,n,r){const s=n[t],{length:o,position:i}=pe[t],c=s.current,u=n.time;s.current=e[`scroll${i}`],s.scrollLength=e[`scroll${o}`]-e[`client${o}`],s.offset.length=0,s.offset[0]=0,s.offset[1]=s.scrollLength,s.progress=a(0,s.scrollLength,s.current);const l=r-u;var f,g;s.velocity=l>50?0:(f=s.current-c,(g=l)?f*(1e3/g):0)}const ve={start:0,center:.5,end:1};function ye(e,t,n=0){let r=0;if(e in ve&&(e=ve[e]),"string"==typeof e){const t=parseFloat(e);e.endsWith("px")?r=t:e.endsWith("%")?e=t/100:e.endsWith("vw")?r=t/100*document.documentElement.clientWidth:e.endsWith("vh")?r=t/100*document.documentElement.clientHeight:e=t}return"number"==typeof e&&(r=t*e),n+r}const be=[0,0];function we(e,t,n,r){let s=Array.isArray(e)?e:be,o=0,i=0;return"number"==typeof e?s=[e,e]:"string"==typeof e&&(s=(e=e.trim()).includes(" ")?e.split(" "):[e,ve[e]?e:"0"]),o=ye(s[0],n,r),i=ye(s[1],t),o-i}const xe={Enter:[[0,1],[1,1]],Exit:[[0,0],[1,0]],Any:[[1,0],[0,1]],All:[[0,0],[1,1]]},Ee={x:0,y:0};function We(t,n,r){const{offset:s=xe.All}=r,{target:o=t,axis:i="y"}=r,a="y"===i?"height":"width",c=o!==t?function(e,t){const n={x:0,y:0};let r=e;for(;r&&r!==t;)if(ne(r))n.x+=r.offsetLeft,n.y+=r.offsetTop,r=r.offsetParent;else if("svg"===r.tagName){const e=r.getBoundingClientRect();r=r.parentElement;const t=r.getBoundingClientRect();n.x+=e.left-t.left,n.y+=e.top-t.top}else{if(!(r instanceof SVGGraphicsElement))break;{const{x:e,y:t}=r.getBBox();n.x+=e,n.y+=t;let s=null,o=r.parentNode;for(;!s;)"svg"===o.tagName&&(s=o),o=r.parentNode;r=s}}return n}(o,t):Ee,u=o===t?{width:t.scrollWidth,height:t.scrollHeight}:function(e){return"getBBox"in e&&"svg"!==e.tagName?e.getBBox():{width:e.clientWidth,height:e.clientHeight}}(o),l={width:t.clientWidth,height:t.clientHeight};n[i].offset.length=0;let f=!n[i].interpolate;const g=s.length;for(let e=0;e{!function(e,t=e,n){if(n.x.targetOffset=0,n.y.targetOffset=0,t!==e){let r=t;for(;r&&r!==e;)n.x.targetOffset+=r.offsetLeft,n.y.targetOffset+=r.offsetTop,r=r.offsetParent}n.x.targetLength=t===e?t.scrollWidth:t.clientWidth,n.y.targetLength=t===e?t.scrollHeight:t.clientHeight,n.x.containerLength=e.clientWidth,n.y.containerLength=e.clientHeight}(e,r.target,n),function(e,t,n){me(e,"x",t,n),me(e,"y",t,n),t.time=n}(e,n,t),(r.offset||r.target)&&We(e,n,r)},notify:()=>t(n)}}const Me=new WeakMap,Ae=new WeakMap,Be=new WeakMap,Se=e=>e===document.scrollingElement?window:e;function Oe(e,{container:t=document.scrollingElement,...n}={}){if(!t)return s;let r=Be.get(t);r||(r=new Set,Be.set(t,r));const o=Le(t,e,{time:0,x:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0},y:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0}},n);if(r.add(o),!Me.has(t)){const e=()=>{for(const e of r)e.measure(d.timestamp);f.preUpdate(n)},n=()=>{for(const e of r)e.notify()},s=()=>f.read(e);Me.set(t,s);const o=Se(t);window.addEventListener("resize",s,{passive:!0}),t!==document.documentElement&&Ae.set(t,(a=s,"function"==typeof(i=t)?de(i):le(i,a))),o.addEventListener("scroll",s,{passive:!0}),s()}var i,a;const c=Me.get(t);return f.read(c,!1,!0),()=>{g(c);const e=Be.get(t);if(!e)return;if(e.delete(o),e.size)return;const n=Me.get(t);Me.delete(t),n&&(Se(t).removeEventListener("scroll",n),Ae.get(t)?.(),window.removeEventListener("resize",n))}}const Te=new Map;function ze({source:e,container:t,...n}){const{axis:r}=n;e&&(t=e);const s=Te.get(t)??new Map;Te.set(t,s);const o=n.target??"self",i=s.get(o)??{},a=r+(n.offset??[]).join(",");return i[a]||(i[a]=!n.target&&te()?new ScrollTimeline({source:t,axis:r}):function(e){const t={value:0},n=Oe(n=>{t.value=100*n[e.axis].progress},e);return{currentTime:t,cancel:n}}({container:t,...n})),i[a]}function He(e,{axis:t="y",container:n=document.scrollingElement,...r}={}){if(!n)return s;const o={axis:t,container:n,...r};return"function"==typeof e?function(e,t){return function(e){return 2===e.length}(e)?Oe(n=>{e(n[t.axis].progress,n)},t):he(e,ze(t))}(e,o):function(e,t){const n=ze(t);return e.attachTimeline({timeline:t.target?void 0:n,observe:e=>(e.pause(),he(t=>{e.time=e.duration*t},n))})}(e,o)}export{He as scroll}; diff --git a/node_modules/framer-motion/dist/size-rollup-waapi-animate.js b/node_modules/framer-motion/dist/size-rollup-waapi-animate.js new file mode 100644 index 00000000..d1c1461d --- /dev/null +++ b/node_modules/framer-motion/dist/size-rollup-waapi-animate.js @@ -0,0 +1 @@ +function t(t){let e;return()=>(void 0===e&&(e=t()),e)}const e=t=>t,i=t=>1e3*t,n=t=>t/1e3,s=t=>null!==t;class a{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}function o(t){for(let e=1;et.startsWith("--");const l=t(()=>void 0!==window.ScrollTimeline),u={};function h(e,i){const n=t(e);return()=>u[i]??n()}const m=h(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),d=([t,e,i,n])=>`cubic-bezier(${t}, ${e}, ${i}, ${n})`,c={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:d([0,.65,.55,1]),circOut:d([.55,0,1,.45]),backIn:d([.31,.01,.66,-.59]),backOut:d([.33,1.53,.69,.99])};function p(t,e){return t?"function"==typeof t?m()?((t,e,i=10)=>{let n="";const s=Math.max(Math.round(e/i),2);for(let e=0;eArray.isArray(t)&&"number"==typeof t[0])(t)?d(t):Array.isArray(t)?t.map(t=>p(t,e)||c.easeOut):c[t]:void 0}class f extends a{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:e,name:i,keyframes:n,pseudoElement:a,allowFlatten:o=!1,finalKeyframe:l,onComplete:u}=t;this.isPseudoElement=Boolean(a),this.allowFlatten=o,this.options=t,t.type;const h=function({type:t,...e}){return function(t){return"function"==typeof t&&"applyToOptions"in t}(t)&&m()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}(t);this.animation=function(t,e,i,{delay:n=0,duration:s=300,repeat:a=0,repeatType:o="loop",ease:r="easeOut",times:l}={},u){const h={[e]:i};l&&(h.offset=l);const m=p(r,s);Array.isArray(m)&&(h.easing=m);const d={delay:n,duration:s,easing:Array.isArray(m)?"linear":m,fill:"both",iterations:a+1,direction:"reverse"===o?"alternate":"normal"};return u&&(d.pseudoElement=u),t.animate(h,d)}(e,i,n,h,a),!1===h.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!a){const t=function(t,{repeat:e,repeatType:i="loop"},n,a=1){const o=t.filter(s),r=a<0||e&&"loop"!==i&&e%2==1?0:o.length-1;return r&&void 0!==n?n:o[r]}(n,this.options,l,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,i){r(e)?t.style.setProperty(e,i):t.style[e]=i}(e,i,t),this.animation.cancel()}u?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return n(Number(t))}get time(){return n(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=i(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:i}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&l()?(this.animation.timeline=t,e):i(this)}}class y{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let i=0;ie.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class g extends y{then(t,e){return this.finished.finally(t).then(()=>{})}}const T=new WeakMap,b=(t,e="")=>`${t}:${e}`;function A(t){const e=T.get(t)||new Map;return T.set(t,e),e}function w(t,e){return t?.[e]??t?.default??t}const v=new Set(["borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderRadius","radius","borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius","width","maxWidth","height","maxHeight","top","right","bottom","left","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","backgroundPositionX","backgroundPositionY"]);function E(t,e){for(let i=0;ie.delete(i)),u.push(s)}return u}const k=t=>function(e,i,n){return new g(S(e,i,n,t))},M=k();export{M as animateMini,k as createScopedWaapiAnimate}; diff --git a/node_modules/framer-motion/dist/types.d-D0HXPxHm.d.ts b/node_modules/framer-motion/dist/types.d-D0HXPxHm.d.ts new file mode 100644 index 00000000..e2218a7f --- /dev/null +++ b/node_modules/framer-motion/dist/types.d-D0HXPxHm.d.ts @@ -0,0 +1,951 @@ +/// +import * as React$1 from 'react'; +import { CSSProperties, PropsWithoutRef, RefAttributes, JSX, SVGAttributes } from 'react'; +import * as react_jsx_runtime from 'react/jsx-runtime'; +import * as motion_dom from 'motion-dom'; +import { TransformProperties, MotionNodeOptions, MotionValue, SVGPathProperties, Transition, JSAnimation, ValueTransition, TargetAndTransition, AnyResolvedKeyframe, KeyframeResolver, AnimationDefinition, Batcher } from 'motion-dom'; +import { TransformPoint, Box, Delta, Point, Axis } from 'motion-utils'; + +/** + * Either a string, or array of strings, that reference variants defined via the `variants` prop. + * @public + */ +type VariantLabels = string | string[]; + +type MotionValueString = MotionValue; +type MotionValueNumber = MotionValue; +type MotionValueAny = MotionValue; +type AnyMotionValue = MotionValueNumber | MotionValueString | MotionValueAny; +type MotionValueHelper = T | AnyMotionValue; +type MakeMotionHelper = { + [K in keyof T]: MotionValueHelper; +}; +type MakeCustomValueTypeHelper = MakeMotionHelper; +type MakeMotion = MakeCustomValueTypeHelper; +type MotionCSS = MakeMotion>; +/** + * @public + */ +type MotionTransform = MakeMotion; +type MotionSVGProps = MakeMotion; +/** + * @public + */ +interface MotionStyle extends MotionCSS, MotionTransform, MotionSVGProps { +} +/** + * Props for `motion` components. + * + * @public + */ +interface MotionProps extends MotionNodeOptions { + /** + * + * The React DOM `style` prop, enhanced with support for `MotionValue`s and separate `transform` values. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * + * return + * } + * ``` + */ + style?: MotionStyle; + children?: React.ReactNode | MotionValueNumber | MotionValueString; +} + +type ReducedMotionConfig = "always" | "never" | "user"; +/** + * @public + */ +interface MotionConfigContext { + /** + * Internal, exported only for usage in Framer + */ + transformPagePoint: TransformPoint; + /** + * Internal. Determines whether this is a static context ie the Framer canvas. If so, + * it'll disable all dynamic functionality. + */ + isStatic: boolean; + /** + * Defines a new default transition for the entire tree. + * + * @public + */ + transition?: Transition; + /** + * If true, will respect the device prefersReducedMotion setting by switching + * transform animations off. + * + * @public + */ + reducedMotion?: ReducedMotionConfig; + /** + * A custom `nonce` attribute used when wanting to enforce a Content Security Policy (CSP). + * For more details see: + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src#unsafe_inline_styles + * + * @public + */ + nonce?: string; +} +/** + * @public + */ +declare const MotionConfigContext: React$1.Context; + +/** + * @public + */ +interface PresenceContextProps { + id: string; + isPresent: boolean; + register: (id: string | number) => () => void; + onExitComplete?: (id: string | number) => void; + initial?: false | VariantLabels; + custom?: any; +} +/** + * @public + */ +declare const PresenceContext: React$1.Context; + +interface SwitchLayoutGroup { + register?: (member: IProjectionNode) => void; + deregister?: (member: IProjectionNode) => void; +} +type InitialPromotionConfig = { + /** + * The initial transition to use when the elements in this group mount (and automatically promoted). + * Subsequent updates should provide a transition in the promote method. + */ + transition?: Transition; + /** + * If the follow tree should preserve its opacity when the lead is promoted on mount + */ + shouldPreserveFollowOpacity?: (member: IProjectionNode) => boolean; +}; +type SwitchLayoutGroupContext = SwitchLayoutGroup & InitialPromotionConfig; +/** + * Internal, exported only for usage in Framer + */ +declare const SwitchLayoutGroupContext: React$1.Context; + +interface WithDepth { + depth: number; +} + +declare class FlatTree { + private children; + private isDirty; + add(child: WithDepth): void; + remove(child: WithDepth): void; + forEach(callback: (child: WithDepth) => void): void; +} + +declare class NodeStack { + lead?: IProjectionNode; + prevLead?: IProjectionNode; + members: IProjectionNode[]; + add(node: IProjectionNode): void; + remove(node: IProjectionNode): void; + relegate(node: IProjectionNode): boolean; + promote(node: IProjectionNode, preserveFollowOpacity?: boolean): void; + exitAnimationComplete(): void; + scheduleRender(): void; + /** + * Clear any leads that have been removed this render to prevent them from being + * used in future animations and to prevent memory leaks + */ + removeLeadSnapshot(): void; +} + +interface Measurements { + animationId: number; + measuredBox: Box; + layoutBox: Box; + latestValues: ResolvedValues; + source: number; +} +type Phase = "snapshot" | "measure"; +interface ScrollMeasurements { + animationId: number; + phase: Phase; + offset: Point; + isRoot: boolean; + wasRoot: boolean; +} +type LayoutEvents = "willUpdate" | "didUpdate" | "beforeMeasure" | "measure" | "projectionUpdate" | "animationStart" | "animationComplete"; +interface IProjectionNode { + id: number; + animationId: number; + animationCommitId: number; + parent?: IProjectionNode; + relativeParent?: IProjectionNode; + root?: IProjectionNode; + children: Set; + path: IProjectionNode[]; + nodes?: FlatTree; + depth: number; + instance: I | undefined; + mount: (node: I, isLayoutDirty?: boolean) => void; + unmount: () => void; + options: ProjectionNodeOptions; + setOptions(options: ProjectionNodeOptions): void; + layout?: Measurements; + snapshot?: Measurements; + target?: Box; + relativeTarget?: Box; + relativeTargetOrigin?: Box; + targetDelta?: Delta; + targetWithTransforms?: Box; + scroll?: ScrollMeasurements; + treeScale?: Point; + projectionDelta?: Delta; + projectionDeltaWithTransform?: Delta; + latestValues: ResolvedValues; + isLayoutDirty: boolean; + isProjectionDirty: boolean; + isSharedProjectionDirty: boolean; + isTransformDirty: boolean; + resolvedRelativeTargetAt?: number; + shouldResetTransform: boolean; + prevTransformTemplateValue: string | undefined; + isUpdateBlocked(): boolean; + updateManuallyBlocked: boolean; + updateBlockedByResize: boolean; + blockUpdate(): void; + unblockUpdate(): void; + isUpdating: boolean; + needsReset: boolean; + startUpdate(): void; + willUpdate(notifyListeners?: boolean): void; + didUpdate(): void; + measure(removeTransform?: boolean): Measurements; + measurePageBox(): Box; + updateLayout(): void; + updateSnapshot(): void; + clearSnapshot(): void; + updateScroll(phase?: Phase): void; + scheduleUpdateProjection(): void; + scheduleCheckAfterUnmount(): void; + checkUpdateFailed(): void; + sharedNodes: Map; + registerSharedNode(id: string, node: IProjectionNode): void; + getStack(): NodeStack | undefined; + isVisible: boolean; + hide(): void; + show(): void; + scheduleRender(notifyAll?: boolean): void; + getClosestProjectingParent(): IProjectionNode | undefined; + setTargetDelta(delta: Delta): void; + resetTransform(): void; + resetSkewAndRotation(): void; + applyTransform(box: Box, transformOnly?: boolean): Box; + resolveTargetDelta(force?: boolean): void; + calcProjection(): void; + applyProjectionStyles(targetStyle: CSSStyleDeclaration, styleProp?: MotionStyle): void; + clearMeasurements(): void; + resetTree(): void; + isProjecting(): boolean; + animationValues?: ResolvedValues; + currentAnimation?: JSAnimation; + isTreeAnimating?: boolean; + isAnimationBlocked?: boolean; + isTreeAnimationBlocked: () => boolean; + setAnimationOrigin(delta: Delta): void; + startAnimation(transition: ValueTransition): void; + finishAnimation(): void; + hasCheckedOptimisedAppear: boolean; + isLead(): boolean; + promote(options?: { + needsReset?: boolean; + transition?: Transition; + preserveFollowOpacity?: boolean; + }): void; + relegate(): boolean; + resumeFrom?: IProjectionNode; + resumingFrom?: IProjectionNode; + isPresent?: boolean; + addEventListener(name: LayoutEvents, handler: any): VoidFunction; + notifyListeners(name: LayoutEvents, ...args: any): void; + hasListeners(name: LayoutEvents): boolean; + hasTreeAnimated: boolean; + preserveOpacity?: boolean; +} +interface ProjectionNodeOptions { + animate?: boolean; + layoutScroll?: boolean; + layoutRoot?: boolean; + alwaysMeasureLayout?: boolean; + onExitComplete?: VoidFunction; + animationType?: "size" | "position" | "both" | "preserve-aspect"; + layoutId?: string; + layout?: boolean | string; + visualElement?: VisualElement; + crossfade?: boolean; + transition?: Transition; + initialPromotionConfig?: InitialPromotionConfig; +} + +type AnimationType = "animate" | "whileHover" | "whileTap" | "whileDrag" | "whileFocus" | "whileInView" | "exit"; + +type VisualElementAnimationOptions = { + delay?: number; + transitionOverride?: Transition; + custom?: any; + type?: AnimationType; +}; + +interface AnimationState { + animateChanges: (type?: AnimationType) => Promise; + setActive: (type: AnimationType, isActive: boolean, options?: VisualElementAnimationOptions) => Promise; + setAnimateFunction: (fn: any) => void; + getState: () => { + [key: string]: AnimationTypeState; + }; + reset: () => void; +} +interface AnimationTypeState { + isActive: boolean; + protectedKeys: { + [key: string]: true; + }; + needsAnimating: { + [key: string]: boolean; + }; + prevResolvedValues: { + [key: string]: any; + }; + prevProp?: VariantLabels | TargetAndTransition; +} + +/** + * A VisualElement is an imperative abstraction around UI elements such as + * HTMLElement, SVGElement, Three.Object3D etc. + */ +declare abstract class VisualElement { + /** + * VisualElements are arranged in trees mirroring that of the React tree. + * Each type of VisualElement has a unique name, to detect when we're crossing + * type boundaries within that tree. + */ + abstract type: string; + /** + * An `Array.sort` compatible function that will compare two Instances and + * compare their respective positions within the tree. + */ + abstract sortInstanceNodePosition(a: Instance, b: Instance): number; + /** + * Measure the viewport-relative bounding box of the Instance. + */ + abstract measureInstanceViewportBox(instance: Instance, props: MotionProps & Partial): Box; + /** + * When a value has been removed from all animation props we need to + * pick a target to animate back to. For instance, for HTMLElements + * we can look in the style prop. + */ + abstract getBaseTargetFromProps(props: MotionProps, key: string): AnyResolvedKeyframe | undefined | MotionValue; + /** + * When we first animate to a value we need to animate it *from* a value. + * Often this have been specified via the initial prop but it might be + * that the value needs to be read from the Instance. + */ + abstract readValueFromInstance(instance: Instance, key: string, options: Options): AnyResolvedKeyframe | null | undefined; + /** + * When a value has been removed from the VisualElement we use this to remove + * it from the inherting class' unique render state. + */ + abstract removeValueFromRenderState(key: string, renderState: RenderState): void; + /** + * Run before a React or VisualElement render, builds the latest motion + * values into an Instance-specific format. For example, HTMLVisualElement + * will use this step to build `style` and `var` values. + */ + abstract build(renderState: RenderState, latestValues: ResolvedValues, props: MotionProps): void; + /** + * Apply the built values to the Instance. For example, HTMLElements will have + * styles applied via `setProperty` and the style attribute, whereas SVGElements + * will have values applied to attributes. + */ + abstract renderInstance(instance: Instance, renderState: RenderState, styleProp?: MotionStyle, projection?: IProjectionNode): void; + /** + * This method is called when a transform property is bound to a motion value. + * It's currently used to measure SVG elements when a new transform property is bound. + */ + onBindTransform?(): void; + /** + * If the component child is provided as a motion value, handle subscriptions + * with the renderer-specific VisualElement. + */ + handleChildMotionValue?(): void; + /** + * This method takes React props and returns found MotionValues. For example, HTML + * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays. + * + * This isn't an abstract method as it needs calling in the constructor, but it is + * intended to be one. + */ + scrapeMotionValuesFromProps(_props: MotionProps, _prevProps: MotionProps, _visualElement: VisualElement): { + [key: string]: MotionValue | AnyResolvedKeyframe; + }; + /** + * A reference to the current underlying Instance, e.g. a HTMLElement + * or Three.Mesh etc. + */ + current: Instance | null; + /** + * A reference to the parent VisualElement (if exists). + */ + parent: VisualElement | undefined; + /** + * A set containing references to this VisualElement's children. + */ + children: Set>; + /** + * The depth of this VisualElement within the overall VisualElement tree. + */ + depth: number; + /** + * The current render state of this VisualElement. Defined by inherting VisualElements. + */ + renderState: RenderState; + /** + * An object containing the latest static values for each of this VisualElement's + * MotionValues. + */ + latestValues: ResolvedValues; + /** + * Determine what role this visual element should take in the variant tree. + */ + isVariantNode: boolean; + isControllingVariants: boolean; + /** + * If this component is part of the variant tree, it should track + * any children that are also part of the tree. This is essentially + * a shadow tree to simplify logic around how to stagger over children. + */ + variantChildren?: Set; + /** + * Decides whether this VisualElement should animate in reduced motion + * mode. + * + * TODO: This is currently set on every individual VisualElement but feels + * like it could be set globally. + */ + shouldReduceMotion: boolean | null; + /** + * Normally, if a component is controlled by a parent's variants, it can + * rely on that ancestor to trigger animations further down the tree. + * However, if a component is created after its parent is mounted, the parent + * won't trigger that mount animation so the child needs to. + * + * TODO: This might be better replaced with a method isParentMounted + */ + manuallyAnimateOnMount: boolean; + /** + * This can be set by AnimatePresence to force components that mount + * at the same time as it to mount as if they have initial={false} set. + */ + blockInitialAnimation: boolean; + /** + * A reference to this VisualElement's projection node, used in layout animations. + */ + projection?: IProjectionNode; + /** + * A map of all motion values attached to this visual element. Motion + * values are source of truth for any given animated value. A motion + * value might be provided externally by the component via props. + */ + values: Map>; + /** + * The AnimationState, this is hydrated by the animation Feature. + */ + animationState?: AnimationState; + KeyframeResolver: typeof KeyframeResolver; + /** + * The options used to create this VisualElement. The Options type is defined + * by the inheriting VisualElement and is passed straight through to the render functions. + */ + readonly options: Options; + /** + * A reference to the latest props provided to the VisualElement's host React component. + */ + props: MotionProps; + prevProps?: MotionProps; + presenceContext: PresenceContextProps | null; + prevPresenceContext?: PresenceContextProps | null; + /** + * Cleanup functions for active features (hover/tap/exit etc) + */ + private features; + /** + * A map of every subscription that binds the provided or generated + * motion values onChange listeners to this visual element. + */ + private valueSubscriptions; + /** + * A reference to the ReducedMotionConfig passed to the VisualElement's host React component. + */ + private reducedMotionConfig; + /** + * On mount, this will be hydrated with a callback to disconnect + * this visual element from its parent on unmount. + */ + private removeFromVariantTree; + /** + * A reference to the previously-provided motion values as returned + * from scrapeMotionValuesFromProps. We use the keys in here to determine + * if any motion values need to be removed after props are updated. + */ + private prevMotionValues; + /** + * When values are removed from all animation props we need to search + * for a fallback value to animate to. These values are tracked in baseTarget. + */ + private baseTarget; + /** + * Create an object of the values we initially animated from (if initial prop present). + */ + private initialValues; + /** + * An object containing a SubscriptionManager for each active event. + */ + private events; + /** + * An object containing an unsubscribe function for each prop event subscription. + * For example, every "Update" event can have multiple subscribers via + * VisualElement.on(), but only one of those can be defined via the onUpdate prop. + */ + private propEventSubscriptions; + constructor({ parent, props, presenceContext, reducedMotionConfig, blockInitialAnimation, visualState, }: VisualElementOptions, options?: Options); + mount(instance: Instance): void; + unmount(): void; + private bindToMotionValue; + sortNodePosition(other: VisualElement): number; + updateFeatures(): void; + notifyUpdate: () => void; + triggerBuild(): void; + render: () => void; + private renderScheduledAt; + scheduleRender: () => void; + /** + * Measure the current viewport box with or without transforms. + * Only measures axis-aligned boxes, rotate and skew must be manually + * removed with a re-render to work. + */ + measureViewportBox(): Box; + getStaticValue(key: string): AnyResolvedKeyframe; + setStaticValue(key: string, value: AnyResolvedKeyframe): void; + /** + * Update the provided props. Ensure any newly-added motion values are + * added to our map, old ones removed, and listeners updated. + */ + update(props: MotionProps, presenceContext: PresenceContextProps | null): void; + getProps(): MotionProps; + /** + * Returns the variant definition with a given name. + */ + getVariant(name: string): motion_dom.Variant | undefined; + /** + * Returns the defined default transition on this component. + */ + getDefaultTransition(): motion_dom.Transition | undefined; + getTransformPagePoint(): any; + getClosestVariantNode(): VisualElement | undefined; + /** + * Add a child visual element to our set of children. + */ + addVariantChild(child: VisualElement): (() => boolean) | undefined; + /** + * Add a motion value and bind it to this visual element. + */ + addValue(key: string, value: MotionValue): void; + /** + * Remove a motion value and unbind any active subscriptions. + */ + removeValue(key: string): void; + /** + * Check whether we have a motion value for this key + */ + hasValue(key: string): boolean; + /** + * Get a motion value for this key. If called with a default + * value, we'll create one if none exists. + */ + getValue(key: string): MotionValue | undefined; + getValue(key: string, defaultValue: AnyResolvedKeyframe | null): MotionValue; + /** + * If we're trying to animate to a previously unencountered value, + * we need to check for it in our state and as a last resort read it + * directly from the instance (which might have performance implications). + */ + readValue(key: string, target?: AnyResolvedKeyframe | null): any; + /** + * Set the base target to later animate back to. This is currently + * only hydrated on creation and when we first read a value. + */ + setBaseTarget(key: string, value: AnyResolvedKeyframe): void; + /** + * Find the base target for a value thats been removed from all animation + * props. + */ + getBaseTarget(key: string): ResolvedValues[string] | undefined | null; + on(eventName: EventName, callback: VisualElementEventCallbacks[EventName]): VoidFunction; + notify(eventName: EventName, ...args: any): void; +} + +type ScrapeMotionValuesFromProps = (props: MotionProps, prevProps: MotionProps, visualElement?: VisualElement) => { + [key: string]: MotionValue | AnyResolvedKeyframe; +}; +interface VisualElementOptions { + visualState: VisualState; + parent?: VisualElement; + variantParent?: VisualElement; + presenceContext: PresenceContextProps | null; + props: MotionProps; + blockInitialAnimation?: boolean; + reducedMotionConfig?: ReducedMotionConfig; +} +/** + * A generic set of string/number values + */ +interface ResolvedValues { + [key: string]: AnyResolvedKeyframe; +} +interface VisualElementEventCallbacks { + BeforeLayoutMeasure: () => void; + LayoutMeasure: (layout: Box, prevLayout?: Box) => void; + LayoutUpdate: (layout: Axis, prevLayout: Axis) => void; + Update: (latest: ResolvedValues) => void; + AnimationStart: (definition: AnimationDefinition) => void; + AnimationComplete: (definition: AnimationDefinition) => void; + LayoutAnimationStart: () => void; + LayoutAnimationComplete: () => void; + SetAxisTarget: () => void; + Unmount: () => void; +} +type CreateVisualElement = (Component: string | React.ComponentType>, options: VisualElementOptions) => VisualElement; + +interface VisualState { + renderState: RenderState; + latestValues: ResolvedValues; + onMount?: (instance: Instance) => void; +} +type UseVisualState = (props: MotionProps, isStatic: boolean) => VisualState; +interface UseVisualStateConfig { + scrapeMotionValuesFromProps: ScrapeMotionValuesFromProps; + createRenderState: () => RenderState; +} +declare const makeUseVisualState: (config: UseVisualStateConfig) => UseVisualState; + +declare abstract class Feature { + isMounted: boolean; + node: VisualElement; + constructor(node: VisualElement); + abstract mount(): void; + abstract unmount(): void; + update(): void; +} + +declare function MeasureLayout(props: MotionProps & { + visualElement: VisualElement; +}): react_jsx_runtime.JSX.Element; + +interface FeatureClass { + new (props: Props): Feature; +} +type HydratedFeatureDefinition = { + isEnabled: (props: MotionProps) => boolean; + Feature: FeatureClass; + ProjectionNode?: any; + MeasureLayout?: typeof MeasureLayout; +}; +interface HydratedFeatureDefinitions { + animation?: HydratedFeatureDefinition; + exit?: HydratedFeatureDefinition; + drag?: HydratedFeatureDefinition; + tap?: HydratedFeatureDefinition; + focus?: HydratedFeatureDefinition; + hover?: HydratedFeatureDefinition; + pan?: HydratedFeatureDefinition; + inView?: HydratedFeatureDefinition; + layout?: HydratedFeatureDefinition; +} +type FeatureDefinition = { + isEnabled: HydratedFeatureDefinition["isEnabled"]; + Feature?: HydratedFeatureDefinition["Feature"]; + ProjectionNode?: HydratedFeatureDefinition["ProjectionNode"]; + MeasureLayout?: HydratedFeatureDefinition["MeasureLayout"]; +}; +type FeatureDefinitions = { + [K in keyof HydratedFeatureDefinitions]: FeatureDefinition; +}; +type FeaturePackage = { + Feature?: HydratedFeatureDefinition["Feature"]; + ProjectionNode?: HydratedFeatureDefinition["ProjectionNode"]; + MeasureLayout?: HydratedFeatureDefinition["MeasureLayout"]; +}; +type FeaturePackages = { + [K in keyof HydratedFeatureDefinitions]: FeaturePackage; +}; +interface FeatureBundle extends FeaturePackages { + renderer: CreateVisualElement; +} +type LazyFeatureBundle = () => Promise; +type RenderComponent = (Component: string | React$1.ComponentType>, props: MotionProps, ref: React$1.Ref, visualState: VisualState, isStatic: boolean, visualElement?: VisualElement) => any; + +interface HTMLElements { + a: HTMLAnchorElement; + abbr: HTMLElement; + address: HTMLElement; + area: HTMLAreaElement; + article: HTMLElement; + aside: HTMLElement; + audio: HTMLAudioElement; + b: HTMLElement; + base: HTMLBaseElement; + bdi: HTMLElement; + bdo: HTMLElement; + big: HTMLElement; + blockquote: HTMLQuoteElement; + body: HTMLBodyElement; + br: HTMLBRElement; + button: HTMLButtonElement; + canvas: HTMLCanvasElement; + caption: HTMLElement; + center: HTMLElement; + cite: HTMLElement; + code: HTMLElement; + col: HTMLTableColElement; + colgroup: HTMLTableColElement; + data: HTMLDataElement; + datalist: HTMLDataListElement; + dd: HTMLElement; + del: HTMLModElement; + details: HTMLDetailsElement; + dfn: HTMLElement; + dialog: HTMLDialogElement; + div: HTMLDivElement; + dl: HTMLDListElement; + dt: HTMLElement; + em: HTMLElement; + embed: HTMLEmbedElement; + fieldset: HTMLFieldSetElement; + figcaption: HTMLElement; + figure: HTMLElement; + footer: HTMLElement; + form: HTMLFormElement; + h1: HTMLHeadingElement; + h2: HTMLHeadingElement; + h3: HTMLHeadingElement; + h4: HTMLHeadingElement; + h5: HTMLHeadingElement; + h6: HTMLHeadingElement; + head: HTMLHeadElement; + header: HTMLElement; + hgroup: HTMLElement; + hr: HTMLHRElement; + html: HTMLHtmlElement; + i: HTMLElement; + iframe: HTMLIFrameElement; + img: HTMLImageElement; + input: HTMLInputElement; + ins: HTMLModElement; + kbd: HTMLElement; + keygen: HTMLElement; + label: HTMLLabelElement; + legend: HTMLLegendElement; + li: HTMLLIElement; + link: HTMLLinkElement; + main: HTMLElement; + map: HTMLMapElement; + mark: HTMLElement; + menu: HTMLElement; + menuitem: HTMLElement; + meta: HTMLMetaElement; + meter: HTMLMeterElement; + nav: HTMLElement; + noindex: HTMLElement; + noscript: HTMLElement; + object: HTMLObjectElement; + ol: HTMLOListElement; + optgroup: HTMLOptGroupElement; + option: HTMLOptionElement; + output: HTMLOutputElement; + p: HTMLParagraphElement; + param: HTMLParamElement; + picture: HTMLElement; + pre: HTMLPreElement; + progress: HTMLProgressElement; + q: HTMLQuoteElement; + rp: HTMLElement; + rt: HTMLElement; + ruby: HTMLElement; + s: HTMLElement; + samp: HTMLElement; + search: HTMLElement; + slot: HTMLSlotElement; + script: HTMLScriptElement; + section: HTMLElement; + select: HTMLSelectElement; + small: HTMLElement; + source: HTMLSourceElement; + span: HTMLSpanElement; + strong: HTMLElement; + style: HTMLStyleElement; + sub: HTMLElement; + summary: HTMLElement; + sup: HTMLElement; + table: HTMLTableElement; + template: HTMLTemplateElement; + tbody: HTMLTableSectionElement; + td: HTMLTableDataCellElement; + textarea: HTMLTextAreaElement; + tfoot: HTMLTableSectionElement; + th: HTMLTableHeaderCellElement; + thead: HTMLTableSectionElement; + time: HTMLTimeElement; + title: HTMLTitleElement; + tr: HTMLTableRowElement; + track: HTMLTrackElement; + u: HTMLElement; + ul: HTMLUListElement; + var: HTMLElement; + video: HTMLVideoElement; + wbr: HTMLElement; + webview: HTMLWebViewElement; +} + +interface TransformOrigin { + originX?: number | string; + originY?: number | string; + originZ?: number | string; +} +interface HTMLRenderState { + /** + * A mutable record of transforms we want to apply directly to the rendered Element + * every frame. We use a mutable data structure to reduce GC during animations. + */ + transform: ResolvedValues; + /** + * A mutable record of transform origins we want to apply directly to the rendered Element + * every frame. We use a mutable data structure to reduce GC during animations. + */ + transformOrigin: TransformOrigin; + /** + * A mutable record of styles we want to apply directly to the rendered Element + * every frame. We use a mutable data structure to reduce GC during animations. + */ + style: ResolvedValues; + /** + * A mutable record of CSS variables we want to apply directly to the rendered Element + * every frame. We use a mutable data structure to reduce GC during animations. + */ + vars: ResolvedValues; +} +/** + * @public + */ +type ForwardRefComponent = { + readonly $$typeof: symbol; +} & ((props: PropsWithoutRef

& RefAttributes) => JSX.Element); +type AttributesWithoutMotionProps = { + [K in Exclude]?: Attributes[K]; +}; +/** + * @public + */ +type HTMLMotionProps = AttributesWithoutMotionProps & MotionProps; +/** + * Motion-optimised versions of React's HTML components. + * + * @public + */ +type HTMLMotionComponents = { + [K in keyof HTMLElements]: ForwardRefComponent>; +}; + +type UnionStringArray> = T[number]; +declare const svgElements: readonly ["animate", "circle", "defs", "desc", "ellipse", "g", "image", "line", "filter", "marker", "mask", "metadata", "path", "pattern", "polygon", "polyline", "rect", "stop", "svg", "switch", "symbol", "text", "tspan", "use", "view", "clipPath", "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence", "foreignObject", "linearGradient", "radialGradient", "textPath"]; +type SVGElements = UnionStringArray; + +interface SVGAttributesWithoutMotionProps extends Pick, Exclude, keyof MotionProps>> { +} +/** + * Blanket-accept any SVG attribute as a `MotionValue` + * @public + */ +type SVGAttributesAsMotionValues = MakeMotion>; +type UnwrapSVGFactoryElement = F extends React.SVGProps ? P : never; +/** + * @public + */ +interface SVGMotionProps extends SVGAttributesAsMotionValues, MotionProps { +} +/** + * Motion-optimised versions of React's SVG components. + * + * @public + */ +type SVGMotionComponents = { + [K in SVGElements]: ForwardRefComponent, SVGMotionProps>>; +}; + +interface MotionComponentConfig { + preloadedFeatures?: FeatureBundle; + createVisualElement?: CreateVisualElement; + useRender: RenderComponent; + useVisualState: UseVisualState; + Component: string | React$1.ComponentType>; +} +type MotionComponentProps = { + [K in Exclude]?: Props[K]; +} & MotionProps; +/** + * Create a `motion` component. + * + * This function accepts a Component argument, which can be either a string (ie "div" + * for `motion.div`), or an actual React component. + * + * Alongside this is a config option which provides a way of rendering the provided + * component "offline", or outside the React render cycle. + */ +declare function createRendererMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }: MotionComponentConfig): React$1.ForwardRefExoticComponent>; + +declare const optimizedAppearDataAttribute: "data-framer-appear-id"; + +/** + * Expose only the needed part of the VisualElement interface to + * ensure React types don't end up in the generic DOM bundle. + */ +interface WithAppearProps { + props: { + [optimizedAppearDataAttribute]?: string; + values?: { + [key: string]: MotionValue | MotionValue; + }; + }; +} +type HandoffFunction = (storeId: string, valueName: string, frame: Batcher) => number | null; +/** + * The window global object acts as a bridge between our inline script + * triggering the optimized appear animations, and Motion. + */ +declare global { + interface Window { + MotionHandoffAnimation?: HandoffFunction; + MotionHandoffMarkAsComplete?: (elementId: string) => void; + MotionHandoffIsComplete?: (elementId: string) => boolean; + MotionHasOptimisedAnimation?: (elementId?: string, valueName?: string) => boolean; + MotionCancelOptimisedAnimation?: (elementId?: string, valueName?: string, frame?: Batcher, canResume?: boolean) => void; + MotionCheckAppearSync?: (visualElement: WithAppearProps, valueName: string, value: MotionValue) => VoidFunction | void; + MotionIsMounted?: boolean; + } +} + +type DOMMotionComponents = HTMLMotionComponents & SVGMotionComponents; + +export { type AnimationType as A, type CreateVisualElement as C, type DOMMotionComponents as D, type FeatureBundle as F, type HTMLMotionProps as H, type IProjectionNode as I, type LazyFeatureBundle as L, type MotionProps as M, PresenceContext as P, type ResolvedValues as R, type SVGMotionComponents as S, VisualElement as V, MotionConfigContext as a, type HTMLElements as b, type MotionComponentProps as c, type HTMLMotionComponents as d, type FeaturePackages as e, type VisualElementAnimationOptions as f, type HTMLRenderState as g, type ScrapeMotionValuesFromProps as h, type VisualState as i, createRendererMotionComponent as j, SwitchLayoutGroupContext as k, type MotionStyle as l, makeUseVisualState as m, type MotionTransform as n, optimizedAppearDataAttribute as o, type VariantLabels as p, type ForwardRefComponent as q, type SVGAttributesAsMotionValues as r, type SVGMotionProps as s, FlatTree as t, type HydratedFeatureDefinition as u, type HydratedFeatureDefinitions as v, type FeatureDefinition as w, type FeatureDefinitions as x, type FeaturePackage as y, type RenderComponent as z }; diff --git a/node_modules/framer-motion/dist/types/client.d.ts b/node_modules/framer-motion/dist/types/client.d.ts new file mode 100644 index 00000000..1d32ac0a --- /dev/null +++ b/node_modules/framer-motion/dist/types/client.d.ts @@ -0,0 +1,185 @@ +/// +import { b as HTMLElements, D as DOMMotionComponents, c as MotionComponentProps, q as ForwardRefComponent, H as HTMLMotionProps, s as SVGMotionProps } from '../types.d-D0HXPxHm.js'; +import * as React from 'react'; +import 'react/jsx-runtime'; +import 'motion-dom'; +import 'motion-utils'; + +declare const createMotionComponent: (Component: string | TagName | React.ComponentType, { forwardMotionProps }?: { + forwardMotionProps: boolean; +}) => TagName extends "symbol" | "animate" | "clipPath" | "filter" | "marker" | "mask" | "path" | "text" | "circle" | "stop" | keyof HTMLElements | "svg" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "switch" | "textPath" | "tspan" | "use" | "view" ? DOMMotionComponents[TagName] : React.ComponentType, "children"> & { + children?: ("children" extends keyof Props ? Props[keyof Props & "children"] | MotionComponentProps[keyof Props & "children"] : MotionComponentProps["children"]) | undefined; +}>; + +/** + * HTML components + */ +declare const MotionA: ForwardRefComponent>; +declare const MotionAbbr: ForwardRefComponent>; +declare const MotionAddress: ForwardRefComponent>; +declare const MotionArea: ForwardRefComponent>; +declare const MotionArticle: ForwardRefComponent>; +declare const MotionAside: ForwardRefComponent>; +declare const MotionAudio: ForwardRefComponent>; +declare const MotionB: ForwardRefComponent>; +declare const MotionBase: ForwardRefComponent>; +declare const MotionBdi: ForwardRefComponent>; +declare const MotionBdo: ForwardRefComponent>; +declare const MotionBig: ForwardRefComponent>; +declare const MotionBlockquote: ForwardRefComponent>; +declare const MotionBody: ForwardRefComponent>; +declare const MotionButton: ForwardRefComponent>; +declare const MotionCanvas: ForwardRefComponent>; +declare const MotionCaption: ForwardRefComponent>; +declare const MotionCite: ForwardRefComponent>; +declare const MotionCode: ForwardRefComponent>; +declare const MotionCol: ForwardRefComponent>; +declare const MotionColgroup: ForwardRefComponent>; +declare const MotionData: ForwardRefComponent>; +declare const MotionDatalist: ForwardRefComponent>; +declare const MotionDd: ForwardRefComponent>; +declare const MotionDel: ForwardRefComponent>; +declare const MotionDetails: ForwardRefComponent>; +declare const MotionDfn: ForwardRefComponent>; +declare const MotionDialog: ForwardRefComponent>; +declare const MotionDiv: ForwardRefComponent>; +declare const MotionDl: ForwardRefComponent>; +declare const MotionDt: ForwardRefComponent>; +declare const MotionEm: ForwardRefComponent>; +declare const MotionEmbed: ForwardRefComponent>; +declare const MotionFieldset: ForwardRefComponent>; +declare const MotionFigcaption: ForwardRefComponent>; +declare const MotionFigure: ForwardRefComponent>; +declare const MotionFooter: ForwardRefComponent>; +declare const MotionForm: ForwardRefComponent>; +declare const MotionH1: ForwardRefComponent>; +declare const MotionH2: ForwardRefComponent>; +declare const MotionH3: ForwardRefComponent>; +declare const MotionH4: ForwardRefComponent>; +declare const MotionH5: ForwardRefComponent>; +declare const MotionH6: ForwardRefComponent>; +declare const MotionHead: ForwardRefComponent>; +declare const MotionHeader: ForwardRefComponent>; +declare const MotionHgroup: ForwardRefComponent>; +declare const MotionHr: ForwardRefComponent>; +declare const MotionHtml: ForwardRefComponent>; +declare const MotionI: ForwardRefComponent>; +declare const MotionIframe: ForwardRefComponent>; +declare const MotionImg: ForwardRefComponent>; +declare const MotionInput: ForwardRefComponent>; +declare const MotionIns: ForwardRefComponent>; +declare const MotionKbd: ForwardRefComponent>; +declare const MotionKeygen: ForwardRefComponent>; +declare const MotionLabel: ForwardRefComponent>; +declare const MotionLegend: ForwardRefComponent>; +declare const MotionLi: ForwardRefComponent>; +declare const MotionLink: ForwardRefComponent>; +declare const MotionMain: ForwardRefComponent>; +declare const MotionMap: ForwardRefComponent>; +declare const MotionMark: ForwardRefComponent>; +declare const MotionMenu: ForwardRefComponent>; +declare const MotionMenuitem: ForwardRefComponent>; +declare const MotionMeter: ForwardRefComponent>; +declare const MotionNav: ForwardRefComponent>; +declare const MotionObject: ForwardRefComponent>; +declare const MotionOl: ForwardRefComponent>; +declare const MotionOptgroup: ForwardRefComponent>; +declare const MotionOption: ForwardRefComponent>; +declare const MotionOutput: ForwardRefComponent>; +declare const MotionP: ForwardRefComponent>; +declare const MotionParam: ForwardRefComponent>; +declare const MotionPicture: ForwardRefComponent>; +declare const MotionPre: ForwardRefComponent>; +declare const MotionProgress: ForwardRefComponent>; +declare const MotionQ: ForwardRefComponent>; +declare const MotionRp: ForwardRefComponent>; +declare const MotionRt: ForwardRefComponent>; +declare const MotionRuby: ForwardRefComponent>; +declare const MotionS: ForwardRefComponent>; +declare const MotionSamp: ForwardRefComponent>; +declare const MotionScript: ForwardRefComponent>; +declare const MotionSection: ForwardRefComponent>; +declare const MotionSelect: ForwardRefComponent>; +declare const MotionSmall: ForwardRefComponent>; +declare const MotionSource: ForwardRefComponent>; +declare const MotionSpan: ForwardRefComponent>; +declare const MotionStrong: ForwardRefComponent>; +declare const MotionStyle: ForwardRefComponent>; +declare const MotionSub: ForwardRefComponent>; +declare const MotionSummary: ForwardRefComponent>; +declare const MotionSup: ForwardRefComponent>; +declare const MotionTable: ForwardRefComponent>; +declare const MotionTbody: ForwardRefComponent>; +declare const MotionTd: ForwardRefComponent>; +declare const MotionTextarea: ForwardRefComponent>; +declare const MotionTfoot: ForwardRefComponent>; +declare const MotionTh: ForwardRefComponent>; +declare const MotionThead: ForwardRefComponent>; +declare const MotionTime: ForwardRefComponent>; +declare const MotionTitle: ForwardRefComponent>; +declare const MotionTr: ForwardRefComponent>; +declare const MotionTrack: ForwardRefComponent>; +declare const MotionU: ForwardRefComponent>; +declare const MotionUl: ForwardRefComponent>; +declare const MotionVideo: ForwardRefComponent>; +declare const MotionWbr: ForwardRefComponent>; +declare const MotionWebview: ForwardRefComponent>; +/** + * SVG components + */ +declare const MotionAnimate: ForwardRefComponent>; +declare const MotionCircle: ForwardRefComponent>; +declare const MotionDefs: ForwardRefComponent>; +declare const MotionDesc: ForwardRefComponent>; +declare const MotionEllipse: ForwardRefComponent>; +declare const MotionG: ForwardRefComponent>; +declare const MotionImage: ForwardRefComponent>; +declare const MotionLine: ForwardRefComponent>; +declare const MotionFilter: ForwardRefComponent>; +declare const MotionMarker: ForwardRefComponent>; +declare const MotionMask: ForwardRefComponent>; +declare const MotionMetadata: ForwardRefComponent>; +declare const MotionPath: ForwardRefComponent>; +declare const MotionPattern: ForwardRefComponent>; +declare const MotionPolygon: ForwardRefComponent>; +declare const MotionPolyline: ForwardRefComponent>; +declare const MotionRect: ForwardRefComponent>; +declare const MotionStop: ForwardRefComponent>; +declare const MotionSvg: ForwardRefComponent>; +declare const MotionSymbol: ForwardRefComponent>; +declare const MotionText: ForwardRefComponent>; +declare const MotionTspan: ForwardRefComponent>; +declare const MotionUse: ForwardRefComponent>; +declare const MotionView: ForwardRefComponent>; +declare const MotionClipPath: ForwardRefComponent>; +declare const MotionFeBlend: ForwardRefComponent>; +declare const MotionFeColorMatrix: ForwardRefComponent>; +declare const MotionFeComponentTransfer: ForwardRefComponent>; +declare const MotionFeComposite: ForwardRefComponent>; +declare const MotionFeConvolveMatrix: ForwardRefComponent>; +declare const MotionFeDiffuseLighting: ForwardRefComponent>; +declare const MotionFeDisplacementMap: ForwardRefComponent>; +declare const MotionFeDistantLight: ForwardRefComponent>; +declare const MotionFeDropShadow: ForwardRefComponent>; +declare const MotionFeFlood: ForwardRefComponent>; +declare const MotionFeFuncA: ForwardRefComponent>; +declare const MotionFeFuncB: ForwardRefComponent>; +declare const MotionFeFuncG: ForwardRefComponent>; +declare const MotionFeFuncR: ForwardRefComponent>; +declare const MotionFeGaussianBlur: ForwardRefComponent>; +declare const MotionFeImage: ForwardRefComponent>; +declare const MotionFeMerge: ForwardRefComponent>; +declare const MotionFeMergeNode: ForwardRefComponent>; +declare const MotionFeMorphology: ForwardRefComponent>; +declare const MotionFeOffset: ForwardRefComponent>; +declare const MotionFePointLight: ForwardRefComponent>; +declare const MotionFeSpecularLighting: ForwardRefComponent>; +declare const MotionFeSpotLight: ForwardRefComponent>; +declare const MotionFeTile: ForwardRefComponent>; +declare const MotionFeTurbulence: ForwardRefComponent>; +declare const MotionForeignObject: ForwardRefComponent>; +declare const MotionLinearGradient: ForwardRefComponent>; +declare const MotionRadialGradient: ForwardRefComponent>; +declare const MotionTextPath: ForwardRefComponent>; + +export { MotionA as a, MotionAbbr as abbr, MotionAddress as address, MotionAnimate as animate, MotionArea as area, MotionArticle as article, MotionAside as aside, MotionAudio as audio, MotionB as b, MotionBase as base, MotionBdi as bdi, MotionBdo as bdo, MotionBig as big, MotionBlockquote as blockquote, MotionBody as body, MotionButton as button, MotionCanvas as canvas, MotionCaption as caption, MotionCircle as circle, MotionCite as cite, MotionClipPath as clipPath, MotionCode as code, MotionCol as col, MotionColgroup as colgroup, createMotionComponent as create, MotionData as data, MotionDatalist as datalist, MotionDd as dd, MotionDefs as defs, MotionDel as del, MotionDesc as desc, MotionDetails as details, MotionDfn as dfn, MotionDialog as dialog, MotionDiv as div, MotionDl as dl, MotionDt as dt, MotionEllipse as ellipse, MotionEm as em, MotionEmbed as embed, MotionFeBlend as feBlend, MotionFeColorMatrix as feColorMatrix, MotionFeComponentTransfer as feComponentTransfer, MotionFeComposite as feComposite, MotionFeConvolveMatrix as feConvolveMatrix, MotionFeDiffuseLighting as feDiffuseLighting, MotionFeDisplacementMap as feDisplacementMap, MotionFeDistantLight as feDistantLight, MotionFeDropShadow as feDropShadow, MotionFeFlood as feFlood, MotionFeFuncA as feFuncA, MotionFeFuncB as feFuncB, MotionFeFuncG as feFuncG, MotionFeFuncR as feFuncR, MotionFeGaussianBlur as feGaussianBlur, MotionFeImage as feImage, MotionFeMerge as feMerge, MotionFeMergeNode as feMergeNode, MotionFeMorphology as feMorphology, MotionFeOffset as feOffset, MotionFePointLight as fePointLight, MotionFeSpecularLighting as feSpecularLighting, MotionFeSpotLight as feSpotLight, MotionFeTile as feTile, MotionFeTurbulence as feTurbulence, MotionFieldset as fieldset, MotionFigcaption as figcaption, MotionFigure as figure, MotionFilter as filter, MotionFooter as footer, MotionForeignObject as foreignObject, MotionForm as form, MotionG as g, MotionH1 as h1, MotionH2 as h2, MotionH3 as h3, MotionH4 as h4, MotionH5 as h5, MotionH6 as h6, MotionHead as head, MotionHeader as header, MotionHgroup as hgroup, MotionHr as hr, MotionHtml as html, MotionI as i, MotionIframe as iframe, MotionImage as image, MotionImg as img, MotionInput as input, MotionIns as ins, MotionKbd as kbd, MotionKeygen as keygen, MotionLabel as label, MotionLegend as legend, MotionLi as li, MotionLine as line, MotionLinearGradient as linearGradient, MotionLink as link, MotionMain as main, MotionMap as map, MotionMark as mark, MotionMarker as marker, MotionMask as mask, MotionMenu as menu, MotionMenuitem as menuitem, MotionMetadata as metadata, MotionMeter as meter, MotionNav as nav, MotionObject as object, MotionOl as ol, MotionOptgroup as optgroup, MotionOption as option, MotionOutput as output, MotionP as p, MotionParam as param, MotionPath as path, MotionPattern as pattern, MotionPicture as picture, MotionPolygon as polygon, MotionPolyline as polyline, MotionPre as pre, MotionProgress as progress, MotionQ as q, MotionRadialGradient as radialGradient, MotionRect as rect, MotionRp as rp, MotionRt as rt, MotionRuby as ruby, MotionS as s, MotionSamp as samp, MotionScript as script, MotionSection as section, MotionSelect as select, MotionSmall as small, MotionSource as source, MotionSpan as span, MotionStop as stop, MotionStrong as strong, MotionStyle as style, MotionSub as sub, MotionSummary as summary, MotionSup as sup, MotionSvg as svg, MotionSymbol as symbol, MotionTable as table, MotionTbody as tbody, MotionTd as td, MotionText as text, MotionTextPath as textPath, MotionTextarea as textarea, MotionTfoot as tfoot, MotionTh as th, MotionThead as thead, MotionTime as time, MotionTitle as title, MotionTr as tr, MotionTrack as track, MotionTspan as tspan, MotionU as u, MotionUl as ul, MotionUse as use, MotionVideo as video, MotionView as view, MotionWbr as wbr, MotionWebview as webview }; diff --git a/node_modules/framer-motion/dist/types/index.d.ts b/node_modules/framer-motion/dist/types/index.d.ts new file mode 100644 index 00000000..46503147 --- /dev/null +++ b/node_modules/framer-motion/dist/types/index.d.ts @@ -0,0 +1,1180 @@ +/// +import * as motion_dom from 'motion-dom'; +import { AnyResolvedKeyframe, OnKeyframesResolved, KeyframeResolver, UnresolvedValueKeyframe, MotionValue, Transition, ElementOrSelector, DOMKeyframesDefinition, AnimationOptions, AnimationPlaybackOptions, AnimationScope, AnimationPlaybackControlsWithThen, ValueAnimationTransition, AnimationPlaybackControls, EventInfo, MotionValueEventCallbacks, SpringOptions, TransformOptions, LegacyAnimationControls, AnimationDefinition } from 'motion-dom'; +export * from 'motion-dom'; +import * as react_jsx_runtime from 'react/jsx-runtime'; +import * as React$1 from 'react'; +import { useEffect, RefObject } from 'react'; +import { F as FeatureBundle, M as MotionProps, a as MotionConfigContext, H as HTMLMotionProps, b as HTMLElements, D as DOMMotionComponents, c as MotionComponentProps, d as HTMLMotionComponents, S as SVGMotionComponents, e as FeaturePackages, V as VisualElement, f as VisualElementAnimationOptions, I as IProjectionNode, R as ResolvedValues, g as HTMLRenderState } from '../types.d-D0HXPxHm.js'; +export { A as AnimationType, C as CreateVisualElement, w as FeatureDefinition, x as FeatureDefinitions, y as FeaturePackage, t as FlatTree, q as ForwardRefComponent, u as HydratedFeatureDefinition, v as HydratedFeatureDefinitions, L as LazyFeatureBundle, l as MotionStyle, n as MotionTransform, P as PresenceContext, z as RenderComponent, r as SVGAttributesAsMotionValues, s as SVGMotionProps, h as ScrapeMotionValuesFromProps, k as SwitchLayoutGroupContext, p as VariantLabels, i as VisualState, j as createRendererMotionComponent, m as makeUseVisualState, o as optimizedAppearDataAttribute } from '../types.d-D0HXPxHm.js'; +import { Easing, EasingFunction, Point, Axis, Box } from 'motion-utils'; +export * from 'motion-utils'; +export { MotionGlobalConfig } from 'motion-utils'; + +type ResolveKeyframes = (keyframes: V[], onComplete: OnKeyframesResolved, name?: string, motionValue?: any) => KeyframeResolver; + +/** + * @public + */ +interface AnimatePresenceProps { + /** + * By passing `initial={false}`, `AnimatePresence` will disable any initial animations on children + * that are present when the component is first rendered. + * + * ```jsx + * + * {isVisible && ( + * + * )} + * + * ``` + * + * @public + */ + initial?: boolean; + /** + * When a component is removed, there's no longer a chance to update its props. So if a component's `exit` + * prop is defined as a dynamic variant and you want to pass a new `custom` prop, you can do so via `AnimatePresence`. + * This will ensure all leaving components animate using the latest data. + * + * @public + */ + custom?: any; + /** + * Fires when all exiting nodes have completed animating out. + * + * @public + */ + onExitComplete?: () => void; + /** + * Determines how to handle entering and exiting elements. + * + * - `"sync"`: Default. Elements animate in and out as soon as they're added/removed. + * - `"popLayout"`: Exiting elements are "popped" from the page layout, allowing sibling + * elements to immediately occupy their new layouts. + * - `"wait"`: Only renders one component at a time. Wait for the exiting component to animate out + * before animating the next component in. + * + * @public + */ + mode?: "sync" | "popLayout" | "wait"; + /** + * Root element to use when injecting styles, used when mode === `"popLayout"`. + * This defaults to document.head but can be overridden e.g. for use in shadow DOM. + */ + root?: HTMLElement | ShadowRoot; + /** + * Internal. Used in Framer to flag that sibling children *shouldn't* re-render as a result of a + * child being removed. + */ + presenceAffectsLayout?: boolean; + /** + * If true, the `AnimatePresence` component will propagate parent exit animations + * to its children. + */ + propagate?: boolean; + /** + * Internal. Set whether to anchor the x position of the exiting element to the left or right + * when using `mode="popLayout"`. + */ + anchorX?: "left" | "right"; +} + +/** + * `AnimatePresence` enables the animation of components that have been removed from the tree. + * + * When adding/removing more than a single child, every child **must** be given a unique `key` prop. + * + * Any `motion` components that have an `exit` property defined will animate out when removed from + * the tree. + * + * ```jsx + * import { motion, AnimatePresence } from 'framer-motion' + * + * export const Items = ({ items }) => ( + * + * {items.map(item => ( + * + * ))} + * + * ) + * ``` + * + * You can sequence exit animations throughout a tree using variants. + * + * If a child contains multiple `motion` components with `exit` props, it will only unmount the child + * once all `motion` components have finished animating out. Likewise, any components using + * `usePresence` all need to call `safeToRemove`. + * + * @public + */ +declare const AnimatePresence: ({ children, custom, initial, onExitComplete, presenceAffectsLayout, mode, propagate, anchorX, root }: React$1.PropsWithChildren) => react_jsx_runtime.JSX.Element | null; + +type InheritOption = boolean | "id"; +interface Props$2 { + id?: string; + inherit?: InheritOption; +} +declare const LayoutGroup: React$1.FunctionComponent>; + +type LazyFeatureBundle = () => Promise; +/** + * @public + */ +interface LazyProps { + children?: React.ReactNode; + /** + * Can be used to provide a feature bundle synchronously or asynchronously. + * + * ```jsx + * // features.js + * import { domAnimation } from "framer-motion" + * export default domAnimation + * + * // index.js + * import { LazyMotion, m } from "framer-motion" + * + * const loadFeatures = () =>import("./features.js") + * .then(res => res.default) + * + * function Component() { + * return ( + * + * + * + * ) + * } + * ``` + * + * @public + */ + features: FeatureBundle | LazyFeatureBundle; + /** + * If `true`, will throw an error if a `motion` component renders within + * a `LazyMotion` component. + * + * ```jsx + * // This component will throw an error that explains using a motion component + * // instead of the m component will break the benefits of code-splitting. + * function Component() { + * return ( + * + * + * + * ) + * } + * ``` + * + * @public + */ + strict?: boolean; +} + +/** + * Used in conjunction with the `m` component to reduce bundle size. + * + * `m` is a version of the `motion` component that only loads functionality + * critical for the initial render. + * + * `LazyMotion` can then be used to either synchronously or asynchronously + * load animation and gesture support. + * + * ```jsx + * // Synchronous loading + * import { LazyMotion, m, domAnimation } from "framer-motion" + * + * function App() { + * return ( + * + * + * + * ) + * } + * + * // Asynchronous loading + * import { LazyMotion, m } from "framer-motion" + * + * function App() { + * return ( + * import('./path/to/domAnimation')}> + * + * + * ) + * } + * ``` + * + * @public + */ +declare function LazyMotion({ children, features, strict }: LazyProps): react_jsx_runtime.JSX.Element; + +type IsValidProp = (key: string) => boolean; +declare function filterProps(props: MotionProps, isDom: boolean, forwardMotionProps: boolean): MotionProps; + +interface MotionConfigProps extends Partial { + children?: React$1.ReactNode; + isValidProp?: IsValidProp; +} +/** + * `MotionConfig` is used to set configuration options for all children `motion` components. + * + * ```jsx + * import { motion, MotionConfig } from "framer-motion" + * + * export function App() { + * return ( + * + * + * + * ) + * } + * ``` + * + * @public + */ +declare function MotionConfig({ children, isValidProp, ...config }: MotionConfigProps): react_jsx_runtime.JSX.Element; + +interface Props$1 { + /** + * A HTML element to render this component as. Defaults to `"ul"`. + * + * @public + */ + as?: keyof HTMLElements; + /** + * The axis to reorder along. By default, items will be draggable on this axis. + * To make draggable on both axes, set `` + * + * @public + */ + axis?: "x" | "y"; + /** + * A callback to fire with the new value order. For instance, if the values + * are provided as a state from `useState`, this could be the set state function. + * + * @public + */ + onReorder: (newOrder: V[]) => void; + /** + * The latest values state. + * + * ```jsx + * function Component() { + * const [items, setItems] = useState([0, 1, 2]) + * + * return ( + * + * {items.map((item) => )} + * + * ) + * } + * ``` + * + * @public + */ + values: V[]; +} +type ReorderGroupProps = Props$1 & Omit, "values"> & React$1.PropsWithChildren<{}>; +declare function ReorderGroupComponent({ children, as, axis, onReorder, values, ...props }: ReorderGroupProps, externalRef?: React$1.ForwardedRef): react_jsx_runtime.JSX.Element; +declare const ReorderGroup: (props: ReorderGroupProps & { + ref?: React$1.ForwardedRef; +}) => ReturnType; + +interface Props { + /** + * A HTML element to render this component as. Defaults to `"li"`. + * + * @public + */ + as?: keyof HTMLElements; + /** + * The value in the list that this component represents. + * + * @public + */ + value: V; + /** + * A subset of layout options primarily used to disable layout="size" + * + * @public + * @default true + */ + layout?: true | "position"; +} +type ReorderItemProps = Props & Omit, "value" | "layout"> & React$1.PropsWithChildren<{}>; +declare function ReorderItemComponent({ children, style, value, as, onDrag, layout, ...props }: ReorderItemProps, externalRef?: React$1.ForwardedRef): react_jsx_runtime.JSX.Element; +declare const ReorderItem: (props: ReorderItemProps & { + ref?: React$1.ForwardedRef; +}) => ReturnType; + +declare namespace namespace_d { + export { ReorderGroup as Group, ReorderItem as Item }; +} + +type ObjectTarget = { + [K in keyof O]?: O[K] | UnresolvedValueKeyframe[]; +}; +type SequenceTime = number | "<" | `+${number}` | `-${number}` | `${string}`; +type SequenceLabel = string; +interface SequenceLabelWithTime { + name: SequenceLabel; + at: SequenceTime; +} +interface At { + at?: SequenceTime; +} +type MotionValueSegment = [ + MotionValue, + UnresolvedValueKeyframe | UnresolvedValueKeyframe[] +]; +type MotionValueSegmentWithTransition = [ + MotionValue, + UnresolvedValueKeyframe | UnresolvedValueKeyframe[], + Transition & At +]; +type DOMSegment = [ElementOrSelector, DOMKeyframesDefinition]; +type DOMSegmentWithTransition = [ + ElementOrSelector, + DOMKeyframesDefinition, + AnimationOptions & At +]; +type ObjectSegment = [O, ObjectTarget]; +type ObjectSegmentWithTransition = [ + O, + ObjectTarget, + AnimationOptions & At +]; +type Segment = ObjectSegment | ObjectSegmentWithTransition | SequenceLabel | SequenceLabelWithTime | MotionValueSegment | MotionValueSegmentWithTransition | DOMSegment | DOMSegmentWithTransition; +type AnimationSequence = Segment[]; +interface SequenceOptions extends AnimationPlaybackOptions { + delay?: number; + duration?: number; + defaultTransition?: Transition; +} +interface AbsoluteKeyframe { + value: AnyResolvedKeyframe | null; + at: number; + easing?: Easing; +} +type ValueSequence = AbsoluteKeyframe[]; +interface SequenceMap { + [key: string]: ValueSequence; +} +type ResolvedAnimationDefinition = { + keyframes: { + [key: string]: UnresolvedValueKeyframe[]; + }; + transition: { + [key: string]: Transition; + }; +}; +type ResolvedAnimationDefinitions = Map; + +/** + * Creates an animation function that is optionally scoped + * to a specific element. + */ +declare function createScopedAnimate(scope?: AnimationScope): { + (sequence: AnimationSequence, options?: SequenceOptions): AnimationPlaybackControlsWithThen; + (value: string | MotionValue, keyframes: string | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: number | MotionValue, keyframes: number | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: V | MotionValue, keyframes: V | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (element: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions): AnimationPlaybackControlsWithThen; + (object: O | O[], keyframes: ObjectTarget, options?: AnimationOptions): AnimationPlaybackControlsWithThen; +}; +declare const animate: { + (sequence: AnimationSequence, options?: SequenceOptions): AnimationPlaybackControlsWithThen; + (value: string | MotionValue, keyframes: string | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: number | MotionValue, keyframes: number | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (value: V | MotionValue, keyframes: V | UnresolvedValueKeyframe[], options?: ValueAnimationTransition): AnimationPlaybackControlsWithThen; + (element: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions): AnimationPlaybackControlsWithThen; + (object: O | O[], keyframes: ObjectTarget, options?: AnimationOptions): AnimationPlaybackControlsWithThen; +}; + +declare const animateMini: (elementOrSelector: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions) => AnimationPlaybackControlsWithThen; + +interface ScrollOptions { + source?: HTMLElement; + container?: Element; + target?: Element; + axis?: "x" | "y"; + offset?: ScrollOffset; +} +type OnScrollProgress = (progress: number) => void; +type OnScrollWithInfo = (progress: number, info: ScrollInfo) => void; +type OnScroll = OnScrollProgress | OnScrollWithInfo; +interface AxisScrollInfo { + current: number; + offset: number[]; + progress: number; + scrollLength: number; + velocity: number; + targetOffset: number; + targetLength: number; + containerLength: number; + interpolatorOffsets?: number[]; + interpolate?: EasingFunction; +} +interface ScrollInfo { + time: number; + x: AxisScrollInfo; + y: AxisScrollInfo; +} +type OnScrollInfo = (info: ScrollInfo) => void; +type SupportedEdgeUnit = "px" | "vw" | "vh" | "%"; +type EdgeUnit = `${number}${SupportedEdgeUnit}`; +type NamedEdges = "start" | "end" | "center"; +type EdgeString = NamedEdges | EdgeUnit | `${number}`; +type Edge = EdgeString | number; +type ProgressIntersection = [number, number]; +type Intersection = `${Edge} ${Edge}`; +type ScrollOffset = Array; +interface ScrollInfoOptions { + container?: Element; + target?: Element; + axis?: "x" | "y"; + offset?: ScrollOffset; +} + +declare function scroll(onScroll: OnScroll | AnimationPlaybackControls, { axis, container, ...options }?: ScrollOptions): VoidFunction; + +declare function scrollInfo(onScroll: OnScrollInfo, { container, ...options }?: ScrollInfoOptions): VoidFunction; + +type ViewChangeHandler = (entry: IntersectionObserverEntry) => void; +type MarginValue = `${number}${"px" | "%"}`; +type MarginType = MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`; +interface InViewOptions { + root?: Element | Document; + margin?: MarginType; + amount?: "some" | "all" | number; +} +declare function inView(elementOrSelector: ElementOrSelector, onStart: (element: Element, entry: IntersectionObserverEntry) => void | ViewChangeHandler, { root, margin: rootMargin, amount }?: InViewOptions): VoidFunction; + +type DelayedFunction = (overshoot: number) => void; +/** + * Timeout defined in ms + */ +declare function delay(callback: DelayedFunction, timeout: number): () => void; + +declare const distance: (a: number, b: number) => number; +declare function distance2D(a: Point, b: Point): number; + +declare const m: ((Component: string | TagName | React$1.ComponentType, { forwardMotionProps }?: { + forwardMotionProps: boolean; +}) => TagName extends "symbol" | "animate" | "clipPath" | "filter" | "marker" | "mask" | "path" | "text" | "circle" | "stop" | keyof HTMLElements | "svg" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "switch" | "textPath" | "tspan" | "use" | "view" ? DOMMotionComponents[TagName] : React$1.ComponentType, "children"> & { + children?: ("children" extends keyof Props ? Props[keyof Props & "children"] | MotionComponentProps[keyof Props & "children"] : MotionComponentProps["children"]) | undefined; +}>) & HTMLMotionComponents & SVGMotionComponents & { + create: (Component: string | TagName | React$1.ComponentType, { forwardMotionProps }?: { + forwardMotionProps: boolean; + }) => TagName extends "symbol" | "animate" | "clipPath" | "filter" | "marker" | "mask" | "path" | "text" | "circle" | "stop" | keyof HTMLElements | "svg" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "switch" | "textPath" | "tspan" | "use" | "view" ? DOMMotionComponents[TagName] : React$1.ComponentType, "children"> & { + children?: ("children" extends keyof Props ? Props[keyof Props & "children"] | MotionComponentProps[keyof Props & "children"] : MotionComponentProps["children"]) | undefined; + }>; +}; + +declare const motion: ((Component: string | TagName | React$1.ComponentType, { forwardMotionProps }?: { + forwardMotionProps: boolean; +}) => TagName extends "symbol" | "animate" | "clipPath" | "filter" | "marker" | "mask" | "path" | "text" | "circle" | "stop" | keyof HTMLElements | "svg" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "switch" | "textPath" | "tspan" | "use" | "view" ? DOMMotionComponents[TagName] : React$1.ComponentType, "children"> & { + children?: ("children" extends keyof Props ? Props[keyof Props & "children"] | MotionComponentProps[keyof Props & "children"] : MotionComponentProps["children"]) | undefined; +}>) & HTMLMotionComponents & SVGMotionComponents & { + create: (Component: string | TagName | React$1.ComponentType, { forwardMotionProps }?: { + forwardMotionProps: boolean; + }) => TagName extends "symbol" | "animate" | "clipPath" | "filter" | "marker" | "mask" | "path" | "text" | "circle" | "stop" | keyof HTMLElements | "svg" | "defs" | "desc" | "ellipse" | "feBlend" | "feColorMatrix" | "feComponentTransfer" | "feComposite" | "feConvolveMatrix" | "feDiffuseLighting" | "feDisplacementMap" | "feDistantLight" | "feDropShadow" | "feFlood" | "feFuncA" | "feFuncB" | "feFuncG" | "feFuncR" | "feGaussianBlur" | "feImage" | "feMerge" | "feMergeNode" | "feMorphology" | "feOffset" | "fePointLight" | "feSpecularLighting" | "feSpotLight" | "feTile" | "feTurbulence" | "foreignObject" | "g" | "image" | "line" | "linearGradient" | "metadata" | "pattern" | "polygon" | "polyline" | "radialGradient" | "rect" | "switch" | "textPath" | "tspan" | "use" | "view" ? DOMMotionComponents[TagName] : React$1.ComponentType, "children"> & { + children?: ("children" extends keyof Props ? Props[keyof Props & "children"] | MotionComponentProps[keyof Props & "children"] : MotionComponentProps["children"]) | undefined; + }>; +}; + +type EventListenerWithPointInfo = (e: PointerEvent, info: EventInfo) => void; +declare const addPointerInfo: (handler: EventListenerWithPointInfo) => EventListener; + +declare function addPointerEvent(target: EventTarget, eventName: string, handler: EventListenerWithPointInfo, options?: AddEventListenerOptions): () => void; + +declare const animations: FeaturePackages; + +declare function calcLength(axis: Axis): number; + +declare const createBox: () => Box; + +declare const isBrowser: boolean; + +declare function useForceUpdate(): [VoidFunction, number]; + +declare const useIsomorphicLayoutEffect: typeof useEffect; + +declare function useUnmountEffect(callback: () => void): void; + +/** + * @public + */ +declare const domAnimation: FeatureBundle; + +/** + * @public + */ +declare const domMax: FeatureBundle; + +/** + * @public + */ +declare const domMin: FeatureBundle; + +declare function useMotionValueEvent>(value: MotionValue, event: EventName, callback: MotionValueEventCallbacks[EventName]): void; + +/** + * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref }) + */ +declare function useElementScroll(ref: RefObject): { + scrollX: motion_dom.MotionValue; + scrollY: motion_dom.MotionValue; + scrollXProgress: motion_dom.MotionValue; + scrollYProgress: motion_dom.MotionValue; +}; + +/** + * @deprecated useViewportScroll is deprecated. Convert to useScroll() + */ +declare function useViewportScroll(): { + scrollX: motion_dom.MotionValue; + scrollY: motion_dom.MotionValue; + scrollXProgress: motion_dom.MotionValue; + scrollYProgress: motion_dom.MotionValue; +}; + +/** + * Combine multiple motion values into a new one using a string template literal. + * + * ```jsx + * import { + * motion, + * useSpring, + * useMotionValue, + * useMotionTemplate + * } from "framer-motion" + * + * function Component() { + * const shadowX = useSpring(0) + * const shadowY = useMotionValue(0) + * const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))` + * + * return + * } + * ``` + * + * @public + */ +declare function useMotionTemplate(fragments: TemplateStringsArray, ...values: Array): MotionValue; + +/** + * Creates a `MotionValue` to track the state and velocity of a value. + * + * Usually, these are created automatically. For advanced use-cases, like use with `useTransform`, you can create `MotionValue`s externally and pass them into the animated component via the `style` prop. + * + * ```jsx + * export const MyComponent = () => { + * const scale = useMotionValue(1) + * + * return + * } + * ``` + * + * @param initial - The initial state. + * + * @public + */ +declare function useMotionValue(initial: T): MotionValue; + +interface UseScrollOptions extends Omit { + container?: RefObject; + target?: RefObject; + layoutEffect?: boolean; +} +declare function useScroll({ container, target, layoutEffect, ...options }?: UseScrollOptions): { + scrollX: motion_dom.MotionValue; + scrollY: motion_dom.MotionValue; + scrollXProgress: motion_dom.MotionValue; + scrollYProgress: motion_dom.MotionValue; +}; + +/** + * Creates a `MotionValue` that, when `set`, will use a spring animation to animate to its new state. + * + * It can either work as a stand-alone `MotionValue` by initialising it with a value, or as a subscriber + * to another `MotionValue`. + * + * @remarks + * + * ```jsx + * const x = useSpring(0, { stiffness: 300 }) + * const y = useSpring(x, { damping: 10 }) + * ``` + * + * @param inputValue - `MotionValue` or number. If provided a `MotionValue`, when the input `MotionValue` changes, the created `MotionValue` will spring towards that value. + * @param springConfig - Configuration options for the spring. + * @returns `MotionValue` + * + * @public + */ +declare function useSpring(source: MotionValue, options?: SpringOptions): MotionValue; +declare function useSpring(source: string, options?: SpringOptions): MotionValue; +declare function useSpring(source: MotionValue, options?: SpringOptions): MotionValue; +declare function useSpring(source: number, options?: SpringOptions): MotionValue; + +declare function useTime(): motion_dom.MotionValue; + +type InputRange = number[]; +type SingleTransformer = (input: I) => O; +type MultiTransformer = (input: I[]) => O; +/** + * Create a `MotionValue` that transforms the output of another `MotionValue` by mapping it from one range of values into another. + * + * @remarks + * + * Given an input range of `[-200, -100, 100, 200]` and an output range of + * `[0, 1, 1, 0]`, the returned `MotionValue` will: + * + * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`. + * - When provided a value between `-100` and `100`, will return `1`. + * - When provided a value between `100` and `200`, will return a value between `1` and `0` + * + * + * The input range must be a linear series of numbers. The output range + * can be any value type supported by Motion: numbers, colors, shadows, etc. + * + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const xRange = [-200, -100, 100, 200] + * const opacityRange = [0, 1, 1, 0] + * const opacity = useTransform(x, xRange, opacityRange) + * + * return ( + * + * ) + * } + * ``` + * + * @param inputValue - `MotionValue` + * @param inputRange - A linear series of numbers (either all increasing or decreasing) + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - + * + * - clamp: boolean. Clamp values to within the given range. Defaults to `true` + * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each. + * + * @returns `MotionValue` + * + * @public + */ +declare function useTransform(value: MotionValue, inputRange: InputRange, outputRange: O[], options?: TransformOptions): MotionValue; +/** + * Create a `MotionValue` that transforms the output of another `MotionValue` through a function. + * In this example, `y` will always be double `x`. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(10) + * const y = useTransform(x, value => value * 2) + * + * return + * } + * ``` + * + * @param input - A `MotionValue` that will pass its latest value through `transform` to update the returned `MotionValue`. + * @param transform - A function that accepts the latest value from `input` and returns a new value. + * @returns `MotionValue` + * + * @public + */ +declare function useTransform(input: MotionValue, transformer: SingleTransformer): MotionValue; +/** + * Pass an array of `MotionValue`s and a function to combine them. In this example, `z` will be the `x` multiplied by `y`. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const y = useMotionValue(0) + * const z = useTransform([x, y], ([latestX, latestY]) => latestX * latestY) + * + * return + * } + * ``` + * + * @param input - An array of `MotionValue`s that will pass their latest values through `transform` to update the returned `MotionValue`. + * @param transform - A function that accepts the latest values from `input` and returns a new value. + * @returns `MotionValue` + * + * @public + */ +declare function useTransform(input: MotionValue[] | MotionValue[] | MotionValue[], transformer: MultiTransformer): MotionValue; +declare function useTransform(transformer: () => O): MotionValue; + +/** + * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes. + * + * ```javascript + * const x = useMotionValue(0) + * const xVelocity = useVelocity(x) + * const xAcceleration = useVelocity(xVelocity) + * ``` + * + * @public + */ +declare function useVelocity(value: MotionValue): MotionValue; + +interface WillChange extends MotionValue { + add(name: string): void; +} + +declare function useWillChange(): WillChange; + +declare class WillChangeMotionValue extends MotionValue implements WillChange { + private isEnabled; + add(name: string): void; + private update; +} + +/** + * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself + * + * TODO: Remove and move to library + */ +declare function resolveMotionValue(value?: AnyResolvedKeyframe | MotionValue): AnyResolvedKeyframe; + +/** + * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting. + * + * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing + * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. + * + * It will actively respond to changes and re-render your components with the latest setting. + * + * ```jsx + * export function Sidebar({ isOpen }) { + * const shouldReduceMotion = useReducedMotion() + * const closedX = shouldReduceMotion ? 0 : "-100%" + * + * return ( + * + * ) + * } + * ``` + * + * @return boolean + * + * @public + */ +declare function useReducedMotion(): boolean | null; + +declare function useReducedMotionConfig(): boolean | null; + +/** + * @public + */ +declare function animationControls(): LegacyAnimationControls; + +declare function useAnimate(): [AnimationScope, { + (sequence: AnimationSequence, options?: SequenceOptions | undefined): motion_dom.AnimationPlaybackControlsWithThen; + (value: string | motion_dom.MotionValue, keyframes: string | motion_dom.UnresolvedValueKeyframe[], options?: motion_dom.ValueAnimationTransition | undefined): motion_dom.AnimationPlaybackControlsWithThen; + (value: number | motion_dom.MotionValue, keyframes: number | motion_dom.UnresolvedValueKeyframe[], options?: motion_dom.ValueAnimationTransition | undefined): motion_dom.AnimationPlaybackControlsWithThen; + (value: V | motion_dom.MotionValue, keyframes: V | motion_dom.UnresolvedValueKeyframe[], options?: motion_dom.ValueAnimationTransition | undefined): motion_dom.AnimationPlaybackControlsWithThen; + (element: motion_dom.ElementOrSelector, keyframes: motion_dom.DOMKeyframesDefinition, options?: motion_dom.AnimationOptions | undefined): motion_dom.AnimationPlaybackControlsWithThen; + (object: O | O[], keyframes: ObjectTarget, options?: motion_dom.AnimationOptions | undefined): motion_dom.AnimationPlaybackControlsWithThen; +}]; + +declare function useAnimateMini(): [AnimationScope, (elementOrSelector: motion_dom.ElementOrSelector, keyframes: motion_dom.DOMKeyframesDefinition, options?: motion_dom.AnimationOptions | undefined) => motion_dom.AnimationPlaybackControlsWithThen]; + +/** + * Creates `LegacyAnimationControls`, which can be used to manually start, stop + * and sequence animations on one or more components. + * + * The returned `LegacyAnimationControls` should be passed to the `animate` property + * of the components you want to animate. + * + * These components can then be animated with the `start` method. + * + * ```jsx + * import * as React from 'react' + * import { motion, useAnimation } from 'framer-motion' + * + * export function MyComponent(props) { + * const controls = useAnimation() + * + * controls.start({ + * x: 100, + * transition: { duration: 0.5 }, + * }) + * + * return + * } + * ``` + * + * @returns Animation controller with `start` and `stop` methods + * + * @public + */ +declare function useAnimationControls(): LegacyAnimationControls; +declare const useAnimation: typeof useAnimationControls; + +declare function animateVisualElement(visualElement: VisualElement, definition: AnimationDefinition, options?: VisualElementAnimationOptions): Promise; + +type SafeToRemove = () => void; +type AlwaysPresent = [true, null]; +type Present = [true]; +type NotPresent = [false, SafeToRemove]; +/** + * When a component is the child of `AnimatePresence`, it can use `usePresence` + * to access information about whether it's still present in the React tree. + * + * ```jsx + * import { usePresence } from "framer-motion" + * + * export const Component = () => { + * const [isPresent, safeToRemove] = usePresence() + * + * useEffect(() => { + * !isPresent && setTimeout(safeToRemove, 1000) + * }, [isPresent]) + * + * return

+ * } + * ``` + * + * If `isPresent` is `false`, it means that a component has been removed the tree, but + * `AnimatePresence` won't really remove it until `safeToRemove` has been called. + * + * @public + */ +declare function usePresence(subscribe?: boolean): AlwaysPresent | Present | NotPresent; +/** + * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present. + * There is no `safeToRemove` function. + * + * ```jsx + * import { useIsPresent } from "framer-motion" + * + * export const Component = () => { + * const isPresent = useIsPresent() + * + * useEffect(() => { + * !isPresent && console.log("I've been removed!") + * }, [isPresent]) + * + * return
+ * } + * ``` + * + * @public + */ +declare function useIsPresent(): boolean; + +declare function usePresenceData(): any; + +/** + * Attaches an event listener directly to the provided DOM element. + * + * Bypassing React's event system can be desirable, for instance when attaching non-passive + * event handlers. + * + * ```jsx + * const ref = useRef(null) + * + * useDomEvent(ref, 'wheel', onWheel, { passive: false }) + * + * return
+ * ``` + * + * @param ref - React.RefObject that's been provided to the element you want to bind the listener to. + * @param eventName - Name of the event you want listen for. + * @param handler - Function to fire when receiving the event. + * @param options - Options to pass to `Event.addEventListener`. + * + * @public + */ +declare function useDomEvent(ref: RefObject, eventName: string, handler?: EventListener | undefined, options?: AddEventListenerOptions): void; + +interface DragControlOptions { + /** + * This distance after which dragging starts and a direction is locked in. + * + * @public + */ + distanceThreshold?: number; + /** + * Whether to immediately snap to the cursor when dragging starts. + * + * @public + */ + snapToCursor?: boolean; +} + +/** + * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ +declare class DragControls { + private componentControls; + /** + * Start a drag gesture on every `motion` component that has this set of drag controls + * passed into it via the `dragControls` prop. + * + * ```jsx + * dragControls.start(e, { + * snapToCursor: true + * }) + * ``` + * + * @param event - PointerEvent + * @param options - Options + * + * @public + */ + start(event: React$1.PointerEvent | PointerEvent, options?: DragControlOptions): void; + /** + * Cancels a drag gesture. + * + * ```jsx + * dragControls.cancel() + * ``` + * + * @public + */ + cancel(): void; + /** + * Stops a drag gesture. + * + * ```jsx + * dragControls.stop() + * ``` + * + * @public + */ + stop(): void; +} +/** + * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop + * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we + * might want to initiate that dragging from a different component than the draggable one. + * + * By creating a `dragControls` using the `useDragControls` hook, we can pass this into + * the draggable component's `dragControls` prop. It exposes a `start` method + * that can start dragging from pointer events on other components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + * + * @public + */ +declare function useDragControls(): DragControls; + +/** + * Checks if a component is a `motion` component. + */ +declare function isMotionComponent(component: React.ComponentType | string): boolean; + +/** + * Unwraps a `motion` component and returns either a string for `motion.div` or + * the React component for `motion(Component)`. + * + * If the component is not a `motion` component it returns undefined. + */ +declare function unwrapMotionComponent(component: React.ComponentType | string): React.ComponentType | string | undefined; + +/** + * Check whether a prop name is a valid `MotionProp` key. + * + * @param key - Name of the property to check + * @returns `true` is key is a valid `MotionProp`. + * + * @public + */ +declare function isValidMotionProp(key: string): boolean; + +type ScaleCorrector = (latest: AnyResolvedKeyframe, node: IProjectionNode) => AnyResolvedKeyframe; +interface ScaleCorrectorDefinition { + correct: ScaleCorrector; + applyTo?: string[]; + isCSSVariable?: boolean; +} +interface ScaleCorrectorMap { + [key: string]: ScaleCorrectorDefinition; +} + +declare function addScaleCorrector(correctors: ScaleCorrectorMap): void; + +declare function useInstantLayoutTransition(): (cb?: (() => void) | undefined) => void; + +declare function useResetProjection(): () => void; + +/** + * Build a CSS transform style from individual x/y/scale etc properties. + * + * This outputs with a default order of transforms/scales/rotations, this can be customised by + * providing a transformTemplate function. + */ +declare function buildTransform(latestValues: ResolvedValues, transform: HTMLRenderState["transform"], transformTemplate?: MotionProps["transformTemplate"]): string; + +declare const visualElementStore: WeakMap>; + +type FrameCallback = (timestamp: number, delta: number) => void; +declare function useAnimationFrame(callback: FrameCallback): void; + +type Cycle = (i?: number) => void; +type CycleState = [T, Cycle]; +/** + * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments. + * + * An index value can be passed to the returned `cycle` function to cycle to a specific index. + * + * ```jsx + * import * as React from "react" + * import { motion, useCycle } from "framer-motion" + * + * export const MyComponent = () => { + * const [x, cycleX] = useCycle(0, 50, 100) + * + * return ( + * cycleX()} + * /> + * ) + * } + * ``` + * + * @param items - items to cycle through + * @returns [currentState, cycleState] + * + * @public + */ +declare function useCycle(...items: T[]): CycleState; + +interface UseInViewOptions extends Omit { + root?: RefObject; + once?: boolean; + amount?: "some" | "all" | number; + initial?: boolean; +} +declare function useInView(ref: RefObject, { root, margin, amount, once, initial, }?: UseInViewOptions): boolean; + +declare function useInstantTransition(): (callback: () => void) => void; +declare function disableInstantTransitions(): void; + +declare function usePageInView(): boolean; + +declare function startOptimizedAppearAnimation(element: HTMLElement, name: string, keyframes: string[] | number[], options: ValueAnimationTransition, onReady?: (animation: Animation) => void): void; + +interface NodeGroup { + add: (node: IProjectionNode) => void; + remove: (node: IProjectionNode) => void; + dirty: VoidFunction; +} + +interface LayoutGroupContextProps { + id?: string; + group?: NodeGroup; + forceRender?: VoidFunction; +} +declare const LayoutGroupContext: React$1.Context; + +interface MotionContextProps { + visualElement?: VisualElement; + initial?: false | string | string[]; + animate?: string | string[]; +} +declare const MotionContext: React$1.Context>; + +/** + * @public + */ +interface ScrollMotionValues { + scrollX: MotionValue; + scrollY: MotionValue; + scrollXProgress: MotionValue; + scrollYProgress: MotionValue; +} + +/** + * This is not an officially supported API and may be removed + * on any version. + */ +declare function useAnimatedState(initialState: any): any[]; + +declare const AnimateSharedLayout: React$1.FunctionComponent>; + +/** + * Note: Still used by components generated by old versions of Framer + * + * @deprecated + */ +declare const DeprecatedLayoutGroupContext: React$1.Context; + +interface ScaleMotionValues { + scaleX: MotionValue; + scaleY: MotionValue; +} +/** + * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse + * of their respective parent scales. + * + * This is useful for undoing the distortion of content when scaling a parent component. + * + * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent. + * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output + * of those instead. + * + * ```jsx + * const MyComponent = () => { + * const { scaleX, scaleY } = useInvertedScale() + * return + * } + * ``` + * + * @deprecated + */ +declare function useInvertedScale(scale?: Partial): ScaleMotionValues; + +export { type AbsoluteKeyframe, AnimatePresence, type AnimatePresenceProps, AnimateSharedLayout, type AnimationSequence, type At, type Cycle, type CycleState, DOMMotionComponents, type DOMSegment, type DOMSegmentWithTransition, type DelayedFunction, DeprecatedLayoutGroupContext, DragControls, FeatureBundle, FeaturePackages, HTMLMotionProps, IProjectionNode, LayoutGroup, LayoutGroupContext, LazyMotion, type LazyProps, MotionConfig, MotionConfigContext, type MotionConfigProps, MotionContext, MotionProps, type MotionValueSegment, type MotionValueSegmentWithTransition, type ObjectSegment, type ObjectSegmentWithTransition, type ObjectTarget, namespace_d as Reorder, type ResolveKeyframes, type ResolvedAnimationDefinition, type ResolvedAnimationDefinitions, ResolvedValues, type ScrollMotionValues, type Segment, type SequenceLabel, type SequenceLabelWithTime, type SequenceMap, type SequenceOptions, type SequenceTime, type UseInViewOptions, type UseScrollOptions, type ValueSequence, VisualElement, WillChangeMotionValue, addPointerEvent, addPointerInfo, addScaleCorrector, animate, animateMini, animateVisualElement, animationControls, animations, buildTransform, calcLength, createBox, createScopedAnimate, delay, disableInstantTransitions, distance, distance2D, domAnimation, domMax, domMin, filterProps, inView, isBrowser, isMotionComponent, isValidMotionProp, m, motion, resolveMotionValue, scroll, scrollInfo, startOptimizedAppearAnimation, unwrapMotionComponent, useAnimate, useAnimateMini, useAnimation, useAnimationControls, useAnimationFrame, useCycle, useAnimatedState as useDeprecatedAnimatedState, useInvertedScale as useDeprecatedInvertedScale, useDomEvent, useDragControls, useElementScroll, useForceUpdate, useInView, useInstantLayoutTransition, useInstantTransition, useIsPresent, useIsomorphicLayoutEffect, useMotionTemplate, useMotionValue, useMotionValueEvent, usePageInView, usePresence, usePresenceData, useReducedMotion, useReducedMotionConfig, useResetProjection, useScroll, useSpring, useTime, useTransform, useUnmountEffect, useVelocity, useViewportScroll, useWillChange, visualElementStore }; diff --git a/node_modules/framer-motion/dom/README.md b/node_modules/framer-motion/dom/README.md new file mode 100644 index 00000000..9f4c437a --- /dev/null +++ b/node_modules/framer-motion/dom/README.md @@ -0,0 +1 @@ +This directory is a fallback for `exports["./dom"]` in the root `framer-motion` `package.json`. diff --git a/node_modules/framer-motion/dom/mini/package.json b/node_modules/framer-motion/dom/mini/package.json new file mode 100644 index 00000000..6d6ee8d7 --- /dev/null +++ b/node_modules/framer-motion/dom/mini/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "types": "../../dist/dom-mini.d.ts", + "main": "../../dist/cjs/dom-mini.js", + "module": "../../dist/es/dom-mini.mjs" +} diff --git a/node_modules/framer-motion/dom/package.json b/node_modules/framer-motion/dom/package.json new file mode 100644 index 00000000..c08f91cd --- /dev/null +++ b/node_modules/framer-motion/dom/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "types": "../dist/dom.d.ts", + "main": "../dist/cjs/dom.js", + "module": "../dist/es/dom.mjs" +} diff --git a/node_modules/framer-motion/m/package.json b/node_modules/framer-motion/m/package.json new file mode 100644 index 00000000..5e76408c --- /dev/null +++ b/node_modules/framer-motion/m/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "types": "../dist/m.d.ts", + "main": "../dist/cjs/m.js", + "module": "../dist/es/m.mjs" +} diff --git a/node_modules/framer-motion/mini/package.json b/node_modules/framer-motion/mini/package.json new file mode 100644 index 00000000..1ed44076 --- /dev/null +++ b/node_modules/framer-motion/mini/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "types": "../dist/mini.d.ts", + "main": "../dist/cjs/mini.js", + "module": "../dist/es/mini.mjs" +} diff --git a/node_modules/framer-motion/package.json b/node_modules/framer-motion/package.json new file mode 100644 index 00000000..8a8ac5d0 --- /dev/null +++ b/node_modules/framer-motion/package.json @@ -0,0 +1,146 @@ +{ + "name": "framer-motion", + "version": "12.23.0", + "description": "A simple and powerful JavaScript animation library", + "main": "dist/cjs/index.js", + "module": "dist/es/index.mjs", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "require": "./dist/cjs/index.js", + "import": "./dist/es/index.mjs", + "default": "./dist/cjs/index.js" + }, + "./debug": { + "types": "./dist/debug.d.ts", + "require": "./dist/cjs/debug.js", + "import": "./dist/es/debug.mjs", + "default": "./dist/cjs/debug.js" + }, + "./dom/mini": { + "types": "./dist/dom-mini.d.ts", + "require": "./dist/cjs/dom-mini.js", + "import": "./dist/es/dom-mini.mjs", + "default": "./dist/cjs/dom-mini.js" + }, + "./dom": { + "types": "./dist/dom.d.ts", + "require": "./dist/cjs/dom.js", + "import": "./dist/es/dom.mjs", + "default": "./dist/cjs/dom.js" + }, + "./client": { + "types": "./dist/types/client.d.ts", + "require": "./dist/cjs/client.js", + "import": "./dist/es/client.mjs", + "default": "./dist/cjs/client.js" + }, + "./m": { + "types": "./dist/m.d.ts", + "require": "./dist/cjs/m.js", + "import": "./dist/es/m.mjs", + "default": "./dist/cjs/m.js" + }, + "./mini": { + "types": "./dist/mini.d.ts", + "require": "./dist/cjs/mini.js", + "import": "./dist/es/mini.mjs", + "default": "./dist/cjs/mini.js" + }, + "./projection": { + "import": "./dist/es/projection.mjs", + "default": "./dist/es/projection.mjs" + }, + "./package.json": "./package.json" + }, + "types": "dist/types/index.d.ts", + "author": "Matt Perry", + "license": "MIT", + "repository": "https://github.com/motiondivision/motion/", + "sideEffects": false, + "keywords": [ + "react animation", + "react", + "pose", + "react pose", + "animation", + "gestures", + "drag", + "spring", + "popmotion", + "framer", + "waapi" + ], + "scripts": { + "eslint": "yarn run lint", + "lint": "yarn eslint src/**/*.ts", + "build": "yarn clean && tsc --noEmitOnError -p . && rollup -c && node ./scripts/check-bundle.js", + "dev": "yarn watch", + "clean": "rm -rf types dist lib", + "test": "yarn test-server && yarn test-client", + "test-client": "jest --config jest.config.json --max-workers=2", + "test-server": "jest --config jest.config.ssr.json", + "prettier": "prettier ./src/* --write", + "watch": "concurrently -c blue,red -n tsc --noEmitOnError ,rollup --kill-others \"tsc --noEmitOnError --watch -p . --preserveWatchOutput\" \"rollup --config --watch --no-watch.clearScreen\"", + "prepack": "yarn build && yarn measure", + "postpublish": "git push --tags", + "measure": "rollup -c ./rollup.size.config.mjs" + }, + "dependencies": { + "motion-dom": "^12.22.0", + "motion-utils": "^12.19.0", + "tslib": "^2.4.0" + }, + "devDependencies": { + "@thednp/dommatrix": "^2.0.11", + "@types/three": "0.137.0", + "three": "0.137.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + }, + "bundlesize": [ + { + "path": "./dist/size-rollup-motion.js", + "maxSize": "34.9 kB" + }, + { + "path": "./dist/size-rollup-m.js", + "maxSize": "6 kB" + }, + { + "path": "./dist/size-rollup-dom-animation.js", + "maxSize": "17.85 kB" + }, + { + "path": "./dist/size-rollup-dom-max.js", + "maxSize": "29.8 kB" + }, + { + "path": "./dist/size-rollup-animate.js", + "maxSize": "19.1 kB" + }, + { + "path": "./dist/size-rollup-scroll.js", + "maxSize": "5.2 kB" + }, + { + "path": "./dist/size-rollup-waapi-animate.js", + "maxSize": "2.26 kB" + } + ], + "gitHead": "a373936bc35696ea3f7335d58aaa6b0be71a1fb7" +} diff --git a/node_modules/motion-dom/LICENSE.md b/node_modules/motion-dom/LICENSE.md new file mode 100644 index 00000000..81110442 --- /dev/null +++ b/node_modules/motion-dom/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 [Motion](https://motion.dev) B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/motion-dom/dist/cjs/index.js b/node_modules/motion-dom/dist/cjs/index.js new file mode 100644 index 00000000..fdaa6a59 --- /dev/null +++ b/node_modules/motion-dom/dist/cjs/index.js @@ -0,0 +1,4773 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var motionUtils = require('motion-utils'); + +const stepsOrder = [ + "setup", // Compute + "read", // Read + "resolveKeyframes", // Write/Read/Write/Read + "preUpdate", // Compute + "update", // Compute + "preRender", // Compute + "render", // Write + "postRender", // Compute +]; + +const statsBuffer = { + value: null, + addProjectionMetrics: null, +}; + +function createRenderStep(runNextFrame, stepName) { + /** + * We create and reuse two queues, one to queue jobs for the current frame + * and one for the next. We reuse to avoid triggering GC after x frames. + */ + let thisFrame = new Set(); + let nextFrame = new Set(); + /** + * Track whether we're currently processing jobs in this step. This way + * we can decide whether to schedule new jobs for this frame or next. + */ + let isProcessing = false; + let flushNextFrame = false; + /** + * A set of processes which were marked keepAlive when scheduled. + */ + const toKeepAlive = new WeakSet(); + let latestFrameData = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + let numCalls = 0; + function triggerCallback(callback) { + if (toKeepAlive.has(callback)) { + step.schedule(callback); + runNextFrame(); + } + numCalls++; + callback(latestFrameData); + } + const step = { + /** + * Schedule a process to run on the next frame. + */ + schedule: (callback, keepAlive = false, immediate = false) => { + const addToCurrentFrame = immediate && isProcessing; + const queue = addToCurrentFrame ? thisFrame : nextFrame; + if (keepAlive) + toKeepAlive.add(callback); + if (!queue.has(callback)) + queue.add(callback); + return callback; + }, + /** + * Cancel the provided callback from running on the next frame. + */ + cancel: (callback) => { + nextFrame.delete(callback); + toKeepAlive.delete(callback); + }, + /** + * Execute all schedule callbacks. + */ + process: (frameData) => { + latestFrameData = frameData; + /** + * If we're already processing we've probably been triggered by a flushSync + * inside an existing process. Instead of executing, mark flushNextFrame + * as true and ensure we flush the following frame at the end of this one. + */ + if (isProcessing) { + flushNextFrame = true; + return; + } + isProcessing = true; + [thisFrame, nextFrame] = [nextFrame, thisFrame]; + // Execute this frame + thisFrame.forEach(triggerCallback); + /** + * If we're recording stats then + */ + if (stepName && statsBuffer.value) { + statsBuffer.value.frameloop[stepName].push(numCalls); + } + numCalls = 0; + // Clear the frame so no callbacks remain. This is to avoid + // memory leaks should this render step not run for a while. + thisFrame.clear(); + isProcessing = false; + if (flushNextFrame) { + flushNextFrame = false; + step.process(frameData); + } + }, + }; + return step; +} + +const maxElapsed = 40; +function createRenderBatcher(scheduleNextBatch, allowKeepAlive) { + let runNextFrame = false; + let useDefaultElapsed = true; + const state = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + const flagRunNextFrame = () => (runNextFrame = true); + const steps = stepsOrder.reduce((acc, key) => { + acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined); + return acc; + }, {}); + const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps; + const processBatch = () => { + const timestamp = motionUtils.MotionGlobalConfig.useManualTiming + ? state.timestamp + : performance.now(); + runNextFrame = false; + if (!motionUtils.MotionGlobalConfig.useManualTiming) { + state.delta = useDefaultElapsed + ? 1000 / 60 + : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1); + } + state.timestamp = timestamp; + state.isProcessing = true; + // Unrolled render loop for better per-frame performance + setup.process(state); + read.process(state); + resolveKeyframes.process(state); + preUpdate.process(state); + update.process(state); + preRender.process(state); + render.process(state); + postRender.process(state); + state.isProcessing = false; + if (runNextFrame && allowKeepAlive) { + useDefaultElapsed = false; + scheduleNextBatch(processBatch); + } + }; + const wake = () => { + runNextFrame = true; + useDefaultElapsed = true; + if (!state.isProcessing) { + scheduleNextBatch(processBatch); + } + }; + const schedule = stepsOrder.reduce((acc, key) => { + const step = steps[key]; + acc[key] = (process, keepAlive = false, immediate = false) => { + if (!runNextFrame) + wake(); + return step.schedule(process, keepAlive, immediate); + }; + return acc; + }, {}); + const cancel = (process) => { + for (let i = 0; i < stepsOrder.length; i++) { + steps[stepsOrder[i]].cancel(process); + } + }; + return { schedule, cancel, state, steps }; +} + +const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : motionUtils.noop, true); + +let now; +function clearTime() { + now = undefined; +} +/** + * An eventloop-synchronous alternative to performance.now(). + * + * Ensures that time measurements remain consistent within a synchronous context. + * Usually calling performance.now() twice within the same synchronous context + * will return different values which isn't useful for animations when we're usually + * trying to sync animations to the same frame. + */ +const time = { + now: () => { + if (now === undefined) { + time.set(frameData.isProcessing || motionUtils.MotionGlobalConfig.useManualTiming + ? frameData.timestamp + : performance.now()); + } + return now; + }, + set: (newTime) => { + now = newTime; + queueMicrotask(clearTime); + }, +}; + +const activeAnimations = { + layout: 0, + mainThread: 0, + waapi: 0, +}; + +const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token); +const isCSSVariableName = +/*@__PURE__*/ checkStringStartsWith("--"); +const startsAsVariableToken = +/*@__PURE__*/ checkStringStartsWith("var(--"); +const isCSSVariableToken = (value) => { + const startsWithToken = startsAsVariableToken(value); + if (!startsWithToken) + return false; + // Ensure any comments are stripped from the value as this can harm performance of the regex. + return singleCssVariableRegex.test(value.split("/*")[0].trim()); +}; +const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu; + +const number = { + test: (v) => typeof v === "number", + parse: parseFloat, + transform: (v) => v, +}; +const alpha = { + ...number, + transform: (v) => motionUtils.clamp(0, 1, v), +}; +const scale = { + ...number, + default: 1, +}; + +// If this number is a decimal, make it just five decimal places +// to avoid exponents +const sanitize = (v) => Math.round(v * 100000) / 100000; + +const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu; + +function isNullish(v) { + return v == null; +} + +const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu; + +/** + * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000, + * but false if a number or multiple colors + */ +const isColorString = (type, testProp) => (v) => { + return Boolean((typeof v === "string" && + singleColorRegex.test(v) && + v.startsWith(type)) || + (testProp && + !isNullish(v) && + Object.prototype.hasOwnProperty.call(v, testProp))); +}; +const splitColor = (aName, bName, cName) => (v) => { + if (typeof v !== "string") + return v; + const [a, b, c, alpha] = v.match(floatRegex); + return { + [aName]: parseFloat(a), + [bName]: parseFloat(b), + [cName]: parseFloat(c), + alpha: alpha !== undefined ? parseFloat(alpha) : 1, + }; +}; + +const clampRgbUnit = (v) => motionUtils.clamp(0, 255, v); +const rgbUnit = { + ...number, + transform: (v) => Math.round(clampRgbUnit(v)), +}; +const rgba = { + test: /*@__PURE__*/ isColorString("rgb", "red"), + parse: /*@__PURE__*/ splitColor("red", "green", "blue"), + transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + + rgbUnit.transform(red) + + ", " + + rgbUnit.transform(green) + + ", " + + rgbUnit.transform(blue) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")", +}; + +function parseHex(v) { + let r = ""; + let g = ""; + let b = ""; + let a = ""; + // If we have 6 characters, ie #FF0000 + if (v.length > 5) { + r = v.substring(1, 3); + g = v.substring(3, 5); + b = v.substring(5, 7); + a = v.substring(7, 9); + // Or we have 3 characters, ie #F00 + } + else { + r = v.substring(1, 2); + g = v.substring(2, 3); + b = v.substring(3, 4); + a = v.substring(4, 5); + r += r; + g += g; + b += b; + a += a; + } + return { + red: parseInt(r, 16), + green: parseInt(g, 16), + blue: parseInt(b, 16), + alpha: a ? parseInt(a, 16) / 255 : 1, + }; +} +const hex = { + test: /*@__PURE__*/ isColorString("#"), + parse: parseHex, + transform: rgba.transform, +}; + +/*#__NO_SIDE_EFFECTS__*/ +const createUnitType = (unit) => ({ + test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1, + parse: parseFloat, + transform: (v) => `${v}${unit}`, +}); +const degrees = /*@__PURE__*/ createUnitType("deg"); +const percent = /*@__PURE__*/ createUnitType("%"); +const px = /*@__PURE__*/ createUnitType("px"); +const vh = /*@__PURE__*/ createUnitType("vh"); +const vw = /*@__PURE__*/ createUnitType("vw"); +const progressPercentage = /*@__PURE__*/ (() => ({ + ...percent, + parse: (v) => percent.parse(v) / 100, + transform: (v) => percent.transform(v * 100), +}))(); + +const hsla = { + test: /*@__PURE__*/ isColorString("hsl", "hue"), + parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"), + transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { + return ("hsla(" + + Math.round(hue) + + ", " + + percent.transform(sanitize(saturation)) + + ", " + + percent.transform(sanitize(lightness)) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")"); + }, +}; + +const color = { + test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v), + parse: (v) => { + if (rgba.test(v)) { + return rgba.parse(v); + } + else if (hsla.test(v)) { + return hsla.parse(v); + } + else { + return hex.parse(v); + } + }, + transform: (v) => { + return typeof v === "string" + ? v + : v.hasOwnProperty("red") + ? rgba.transform(v) + : hsla.transform(v); + }, + getAnimatableNone: (v) => { + const parsed = color.parse(v); + parsed.alpha = 0; + return color.transform(parsed); + }, +}; + +const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu; + +function test(v) { + return (isNaN(v) && + typeof v === "string" && + (v.match(floatRegex)?.length || 0) + + (v.match(colorRegex)?.length || 0) > + 0); +} +const NUMBER_TOKEN = "number"; +const COLOR_TOKEN = "color"; +const VAR_TOKEN = "var"; +const VAR_FUNCTION_TOKEN = "var("; +const SPLIT_TOKEN = "${}"; +// this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex` +const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu; +function analyseComplexValue(value) { + const originalValue = value.toString(); + const values = []; + const indexes = { + color: [], + number: [], + var: [], + }; + const types = []; + let i = 0; + const tokenised = originalValue.replace(complexRegex, (parsedValue) => { + if (color.test(parsedValue)) { + indexes.color.push(i); + types.push(COLOR_TOKEN); + values.push(color.parse(parsedValue)); + } + else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) { + indexes.var.push(i); + types.push(VAR_TOKEN); + values.push(parsedValue); + } + else { + indexes.number.push(i); + types.push(NUMBER_TOKEN); + values.push(parseFloat(parsedValue)); + } + ++i; + return SPLIT_TOKEN; + }); + const split = tokenised.split(SPLIT_TOKEN); + return { values, split, indexes, types }; +} +function parseComplexValue(v) { + return analyseComplexValue(v).values; +} +function createTransformer(source) { + const { split, types } = analyseComplexValue(source); + const numSections = split.length; + return (v) => { + let output = ""; + for (let i = 0; i < numSections; i++) { + output += split[i]; + if (v[i] !== undefined) { + const type = types[i]; + if (type === NUMBER_TOKEN) { + output += sanitize(v[i]); + } + else if (type === COLOR_TOKEN) { + output += color.transform(v[i]); + } + else { + output += v[i]; + } + } + } + return output; + }; +} +const convertNumbersToZero = (v) => typeof v === "number" ? 0 : color.test(v) ? color.getAnimatableNone(v) : v; +function getAnimatableNone$1(v) { + const parsed = parseComplexValue(v); + const transformer = createTransformer(v); + return transformer(parsed.map(convertNumbersToZero)); +} +const complex = { + test, + parse: parseComplexValue, + createTransformer, + getAnimatableNone: getAnimatableNone$1, +}; + +// Adapted from https://gist.github.com/mjackson/5311256 +function hueToRgb(p, q, t) { + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < 1 / 6) + return p + (q - p) * 6 * t; + if (t < 1 / 2) + return q; + if (t < 2 / 3) + return p + (q - p) * (2 / 3 - t) * 6; + return p; +} +function hslaToRgba({ hue, saturation, lightness, alpha }) { + hue /= 360; + saturation /= 100; + lightness /= 100; + let red = 0; + let green = 0; + let blue = 0; + if (!saturation) { + red = green = blue = lightness; + } + else { + const q = lightness < 0.5 + ? lightness * (1 + saturation) + : lightness + saturation - lightness * saturation; + const p = 2 * lightness - q; + red = hueToRgb(p, q, hue + 1 / 3); + green = hueToRgb(p, q, hue); + blue = hueToRgb(p, q, hue - 1 / 3); + } + return { + red: Math.round(red * 255), + green: Math.round(green * 255), + blue: Math.round(blue * 255), + alpha, + }; +} + +function mixImmediate(a, b) { + return (p) => (p > 0 ? b : a); +} + +/* + Value in range from progress + + Given a lower limit and an upper limit, we return the value within + that range as expressed by progress (usually a number from 0 to 1) + + So progress = 0.5 would change + + from -------- to + + to + + from ---- to + + E.g. from = 10, to = 20, progress = 0.5 => 15 + + @param [number]: Lower limit of range + @param [number]: Upper limit of range + @param [number]: The progress between lower and upper limits expressed 0-1 + @return [number]: Value as calculated from progress within range (not limited within range) +*/ +const mixNumber$1 = (from, to, progress) => { + return from + (to - from) * progress; +}; + +// Linear color space blending +// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw +// Demonstrated http://codepen.io/osublake/pen/xGVVaN +const mixLinearColor = (from, to, v) => { + const fromExpo = from * from; + const expo = v * (to * to - fromExpo) + fromExpo; + return expo < 0 ? 0 : Math.sqrt(expo); +}; +const colorTypes = [hex, rgba, hsla]; +const getColorType = (v) => colorTypes.find((type) => type.test(v)); +function asRGBA(color) { + const type = getColorType(color); + motionUtils.warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`); + if (!Boolean(type)) + return false; + let model = type.parse(color); + if (type === hsla) { + // TODO Remove this cast - needed since Motion's stricter typing + model = hslaToRgba(model); + } + return model; +} +const mixColor = (from, to) => { + const fromRGBA = asRGBA(from); + const toRGBA = asRGBA(to); + if (!fromRGBA || !toRGBA) { + return mixImmediate(from, to); + } + const blended = { ...fromRGBA }; + return (v) => { + blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v); + blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v); + blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v); + blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v); + return rgba.transform(blended); + }; +}; + +const invisibleValues = new Set(["none", "hidden"]); +/** + * Returns a function that, when provided a progress value between 0 and 1, + * will return the "none" or "hidden" string only when the progress is that of + * the origin or target. + */ +function mixVisibility(origin, target) { + if (invisibleValues.has(origin)) { + return (p) => (p <= 0 ? origin : target); + } + else { + return (p) => (p >= 1 ? target : origin); + } +} + +function mixNumber(a, b) { + return (p) => mixNumber$1(a, b, p); +} +function getMixer(a) { + if (typeof a === "number") { + return mixNumber; + } + else if (typeof a === "string") { + return isCSSVariableToken(a) + ? mixImmediate + : color.test(a) + ? mixColor + : mixComplex; + } + else if (Array.isArray(a)) { + return mixArray; + } + else if (typeof a === "object") { + return color.test(a) ? mixColor : mixObject; + } + return mixImmediate; +} +function mixArray(a, b) { + const output = [...a]; + const numValues = output.length; + const blendValue = a.map((v, i) => getMixer(v)(v, b[i])); + return (p) => { + for (let i = 0; i < numValues; i++) { + output[i] = blendValue[i](p); + } + return output; + }; +} +function mixObject(a, b) { + const output = { ...a, ...b }; + const blendValue = {}; + for (const key in output) { + if (a[key] !== undefined && b[key] !== undefined) { + blendValue[key] = getMixer(a[key])(a[key], b[key]); + } + } + return (v) => { + for (const key in blendValue) { + output[key] = blendValue[key](v); + } + return output; + }; +} +function matchOrder(origin, target) { + const orderedOrigin = []; + const pointers = { color: 0, var: 0, number: 0 }; + for (let i = 0; i < target.values.length; i++) { + const type = target.types[i]; + const originIndex = origin.indexes[type][pointers[type]]; + const originValue = origin.values[originIndex] ?? 0; + orderedOrigin[i] = originValue; + pointers[type]++; + } + return orderedOrigin; +} +const mixComplex = (origin, target) => { + const template = complex.createTransformer(target); + const originStats = analyseComplexValue(origin); + const targetStats = analyseComplexValue(target); + const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length && + originStats.indexes.color.length === targetStats.indexes.color.length && + originStats.indexes.number.length >= targetStats.indexes.number.length; + if (canInterpolate) { + if ((invisibleValues.has(origin) && + !targetStats.values.length) || + (invisibleValues.has(target) && + !originStats.values.length)) { + return mixVisibility(origin, target); + } + return motionUtils.pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template); + } + else { + motionUtils.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`); + return mixImmediate(origin, target); + } +}; + +function mix(from, to, p) { + if (typeof from === "number" && + typeof to === "number" && + typeof p === "number") { + return mixNumber$1(from, to, p); + } + const mixer = getMixer(from); + return mixer(from, to); +} + +const frameloopDriver = (update) => { + const passTimestamp = ({ timestamp }) => update(timestamp); + return { + start: (keepAlive = true) => frame.update(passTimestamp, keepAlive), + stop: () => cancelFrame(passTimestamp), + /** + * If we're processing this frame we can use the + * framelocked timestamp to keep things in sync. + */ + now: () => (frameData.isProcessing ? frameData.timestamp : time.now()), + }; +}; + +const generateLinearEasing = (easing, duration, // as milliseconds +resolution = 10 // as milliseconds +) => { + let points = ""; + const numPoints = Math.max(Math.round(duration / resolution), 2); + for (let i = 0; i < numPoints; i++) { + points += Math.round(easing(i / (numPoints - 1)) * 10000) / 10000 + ", "; + } + return `linear(${points.substring(0, points.length - 2)})`; +}; + +/** + * Implement a practical max duration for keyframe generation + * to prevent infinite loops + */ +const maxGeneratorDuration = 20000; +function calcGeneratorDuration(generator) { + let duration = 0; + const timeStep = 50; + let state = generator.next(duration); + while (!state.done && duration < maxGeneratorDuration) { + duration += timeStep; + state = generator.next(duration); + } + return duration >= maxGeneratorDuration ? Infinity : duration; +} + +/** + * Create a progress => progress easing function from a generator. + */ +function createGeneratorEasing(options, scale = 100, createGenerator) { + const generator = createGenerator({ ...options, keyframes: [0, scale] }); + const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + return { + type: "keyframes", + ease: (progress) => { + return generator.next(duration * progress).value / scale; + }, + duration: motionUtils.millisecondsToSeconds(duration), + }; +} + +const velocitySampleDuration = 5; // ms +function calcGeneratorVelocity(resolveValue, t, current) { + const prevT = Math.max(t - velocitySampleDuration, 0); + return motionUtils.velocityPerSecond(current - resolveValue(prevT), t - prevT); +} + +const springDefaults = { + // Default spring physics + stiffness: 100, + damping: 10, + mass: 1.0, + velocity: 0.0, + // Default duration/bounce-based options + duration: 800, // in ms + bounce: 0.3, + visualDuration: 0.3, // in seconds + // Rest thresholds + restSpeed: { + granular: 0.01, + default: 2, + }, + restDelta: { + granular: 0.005, + default: 0.5, + }, + // Limits + minDuration: 0.01, // in seconds + maxDuration: 10.0, // in seconds + minDamping: 0.05, + maxDamping: 1, +}; + +const safeMin = 0.001; +function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) { + let envelope; + let derivative; + motionUtils.warning(duration <= motionUtils.secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less"); + let dampingRatio = 1 - bounce; + /** + * Restrict dampingRatio and duration to within acceptable ranges. + */ + dampingRatio = motionUtils.clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio); + duration = motionUtils.clamp(springDefaults.minDuration, springDefaults.maxDuration, motionUtils.millisecondsToSeconds(duration)); + if (dampingRatio < 1) { + /** + * Underdamped spring + */ + envelope = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const a = exponentialDecay - velocity; + const b = calcAngularFreq(undampedFreq, dampingRatio); + const c = Math.exp(-delta); + return safeMin - (a / b) * c; + }; + derivative = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const d = delta * velocity + velocity; + const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration; + const f = Math.exp(-delta); + const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio); + const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1; + return (factor * ((d - e) * f)) / g; + }; + } + else { + /** + * Critically-damped spring + */ + envelope = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (undampedFreq - velocity) * duration + 1; + return -safeMin + a * b; + }; + derivative = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (velocity - undampedFreq) * (duration * duration); + return a * b; + }; + } + const initialGuess = 5 / duration; + const undampedFreq = approximateRoot(envelope, derivative, initialGuess); + duration = motionUtils.secondsToMilliseconds(duration); + if (isNaN(undampedFreq)) { + return { + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + duration, + }; + } + else { + const stiffness = Math.pow(undampedFreq, 2) * mass; + return { + stiffness, + damping: dampingRatio * 2 * Math.sqrt(mass * stiffness), + duration, + }; + } +} +const rootIterations = 12; +function approximateRoot(envelope, derivative, initialGuess) { + let result = initialGuess; + for (let i = 1; i < rootIterations; i++) { + result = result - envelope(result) / derivative(result); + } + return result; +} +function calcAngularFreq(undampedFreq, dampingRatio) { + return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio); +} + +const durationKeys = ["duration", "bounce"]; +const physicsKeys = ["stiffness", "damping", "mass"]; +function isSpringType(options, keys) { + return keys.some((key) => options[key] !== undefined); +} +function getSpringOptions(options) { + let springOptions = { + velocity: springDefaults.velocity, + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + mass: springDefaults.mass, + isResolvedFromDuration: false, + ...options, + }; + // stiffness/damping/mass overrides duration/bounce + if (!isSpringType(options, physicsKeys) && + isSpringType(options, durationKeys)) { + if (options.visualDuration) { + const visualDuration = options.visualDuration; + const root = (2 * Math.PI) / (visualDuration * 1.2); + const stiffness = root * root; + const damping = 2 * + motionUtils.clamp(0.05, 1, 1 - (options.bounce || 0)) * + Math.sqrt(stiffness); + springOptions = { + ...springOptions, + mass: springDefaults.mass, + stiffness, + damping, + }; + } + else { + const derived = findSpring(options); + springOptions = { + ...springOptions, + ...derived, + mass: springDefaults.mass, + }; + springOptions.isResolvedFromDuration = true; + } + } + return springOptions; +} +function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) { + const options = typeof optionsOrVisualDuration !== "object" + ? { + visualDuration: optionsOrVisualDuration, + keyframes: [0, 1], + bounce, + } + : optionsOrVisualDuration; + let { restSpeed, restDelta } = options; + const origin = options.keyframes[0]; + const target = options.keyframes[options.keyframes.length - 1]; + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { done: false, value: origin }; + const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({ + ...options, + velocity: -motionUtils.millisecondsToSeconds(options.velocity || 0), + }); + const initialVelocity = velocity || 0.0; + const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass)); + const initialDelta = target - origin; + const undampedAngularFreq = motionUtils.millisecondsToSeconds(Math.sqrt(stiffness / mass)); + /** + * If we're working on a granular scale, use smaller defaults for determining + * when the spring is finished. + * + * These defaults have been selected emprically based on what strikes a good + * ratio between feeling good and finishing as soon as changes are imperceptible. + */ + const isGranularScale = Math.abs(initialDelta) < 5; + restSpeed || (restSpeed = isGranularScale + ? springDefaults.restSpeed.granular + : springDefaults.restSpeed.default); + restDelta || (restDelta = isGranularScale + ? springDefaults.restDelta.granular + : springDefaults.restDelta.default); + let resolveSpring; + if (dampingRatio < 1) { + const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio); + // Underdamped spring + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + return (target - + envelope * + (((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) / + angularFreq) * + Math.sin(angularFreq * t) + + initialDelta * Math.cos(angularFreq * t))); + }; + } + else if (dampingRatio === 1) { + // Critically damped spring + resolveSpring = (t) => target - + Math.exp(-undampedAngularFreq * t) * + (initialDelta + + (initialVelocity + undampedAngularFreq * initialDelta) * t); + } + else { + // Overdamped spring + const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1); + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + // When performing sinh or cosh values can hit Infinity so we cap them here + const freqForT = Math.min(dampedAngularFreq * t, 300); + return (target - + (envelope * + ((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) * + Math.sinh(freqForT) + + dampedAngularFreq * + initialDelta * + Math.cosh(freqForT))) / + dampedAngularFreq); + }; + } + const generator = { + calculatedDuration: isResolvedFromDuration ? duration || null : null, + next: (t) => { + const current = resolveSpring(t); + if (!isResolvedFromDuration) { + let currentVelocity = t === 0 ? initialVelocity : 0.0; + /** + * We only need to calculate velocity for under-damped springs + * as over- and critically-damped springs can't overshoot, so + * checking only for displacement is enough. + */ + if (dampingRatio < 1) { + currentVelocity = + t === 0 + ? motionUtils.secondsToMilliseconds(initialVelocity) + : calcGeneratorVelocity(resolveSpring, t, current); + } + const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed; + const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta; + state.done = + isBelowVelocityThreshold && isBelowDisplacementThreshold; + } + else { + state.done = t >= duration; + } + state.value = state.done ? target : current; + return state; + }, + toString: () => { + const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30); + return calculatedDuration + "ms " + easing; + }, + toTransition: () => { }, + }; + return generator; +} +spring.applyToOptions = (options) => { + const generatorOptions = createGeneratorEasing(options, 100, spring); + options.ease = generatorOptions.ease; + options.duration = motionUtils.secondsToMilliseconds(generatorOptions.duration); + options.type = "keyframes"; + return options; +}; + +function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) { + const origin = keyframes[0]; + const state = { + done: false, + value: origin, + }; + const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max); + const nearestBoundary = (v) => { + if (min === undefined) + return max; + if (max === undefined) + return min; + return Math.abs(min - v) < Math.abs(max - v) ? min : max; + }; + let amplitude = power * velocity; + const ideal = origin + amplitude; + const target = modifyTarget === undefined ? ideal : modifyTarget(ideal); + /** + * If the target has changed we need to re-calculate the amplitude, otherwise + * the animation will start from the wrong position. + */ + if (target !== ideal) + amplitude = target - origin; + const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant); + const calcLatest = (t) => target + calcDelta(t); + const applyFriction = (t) => { + const delta = calcDelta(t); + const latest = calcLatest(t); + state.done = Math.abs(delta) <= restDelta; + state.value = state.done ? target : latest; + }; + /** + * Ideally this would resolve for t in a stateless way, we could + * do that by always precalculating the animation but as we know + * this will be done anyway we can assume that spring will + * be discovered during that. + */ + let timeReachedBoundary; + let spring$1; + const checkCatchBoundary = (t) => { + if (!isOutOfBounds(state.value)) + return; + timeReachedBoundary = t; + spring$1 = spring({ + keyframes: [state.value, nearestBoundary(state.value)], + velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000 + damping: bounceDamping, + stiffness: bounceStiffness, + restDelta, + restSpeed, + }); + }; + checkCatchBoundary(0); + return { + calculatedDuration: null, + next: (t) => { + /** + * We need to resolve the friction to figure out if we need a + * spring but we don't want to do this twice per frame. So here + * we flag if we updated for this frame and later if we did + * we can skip doing it again. + */ + let hasUpdatedFrame = false; + if (!spring$1 && timeReachedBoundary === undefined) { + hasUpdatedFrame = true; + applyFriction(t); + checkCatchBoundary(t); + } + /** + * If we have a spring and the provided t is beyond the moment the friction + * animation crossed the min/max boundary, use the spring. + */ + if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) { + return spring$1.next(t - timeReachedBoundary); + } + else { + !hasUpdatedFrame && applyFriction(t); + return state; + } + }, + }; +} + +function createMixers(output, ease, customMixer) { + const mixers = []; + const mixerFactory = customMixer || motionUtils.MotionGlobalConfig.mix || mix; + const numMixers = output.length - 1; + for (let i = 0; i < numMixers; i++) { + let mixer = mixerFactory(output[i], output[i + 1]); + if (ease) { + const easingFunction = Array.isArray(ease) ? ease[i] || motionUtils.noop : ease; + mixer = motionUtils.pipe(easingFunction, mixer); + } + mixers.push(mixer); + } + return mixers; +} +/** + * Create a function that maps from a numerical input array to a generic output array. + * + * Accepts: + * - Numbers + * - Colors (hex, hsl, hsla, rgb, rgba) + * - Complex (combinations of one or more numbers or strings) + * + * ```jsx + * const mixColor = interpolate([0, 1], ['#fff', '#000']) + * + * mixColor(0.5) // 'rgba(128, 128, 128, 1)' + * ``` + * + * TODO Revisit this approach once we've moved to data models for values, + * probably not needed to pregenerate mixer functions. + * + * @public + */ +function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) { + const inputLength = input.length; + motionUtils.invariant(inputLength === output.length, "Both input and output ranges must be the same length"); + /** + * If we're only provided a single input, we can just make a function + * that returns the output. + */ + if (inputLength === 1) + return () => output[0]; + if (inputLength === 2 && output[0] === output[1]) + return () => output[1]; + const isZeroDeltaRange = input[0] === input[1]; + // If input runs highest -> lowest, reverse both arrays + if (input[0] > input[inputLength - 1]) { + input = [...input].reverse(); + output = [...output].reverse(); + } + const mixers = createMixers(output, ease, mixer); + const numMixers = mixers.length; + const interpolator = (v) => { + if (isZeroDeltaRange && v < input[0]) + return output[0]; + let i = 0; + if (numMixers > 1) { + for (; i < input.length - 2; i++) { + if (v < input[i + 1]) + break; + } + } + const progressInRange = motionUtils.progress(input[i], input[i + 1], v); + return mixers[i](progressInRange); + }; + return isClamp + ? (v) => interpolator(motionUtils.clamp(input[0], input[inputLength - 1], v)) + : interpolator; +} + +function fillOffset(offset, remaining) { + const min = offset[offset.length - 1]; + for (let i = 1; i <= remaining; i++) { + const offsetProgress = motionUtils.progress(0, remaining, i); + offset.push(mixNumber$1(min, 1, offsetProgress)); + } +} + +function defaultOffset(arr) { + const offset = [0]; + fillOffset(offset, arr.length - 1); + return offset; +} + +function convertOffsetToTimes(offset, duration) { + return offset.map((o) => o * duration); +} + +function defaultEasing(values, easing) { + return values.map(() => easing || motionUtils.easeInOut).splice(0, values.length - 1); +} +function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) { + /** + * Easing functions can be externally defined as strings. Here we convert them + * into actual functions. + */ + const easingFunctions = motionUtils.isEasingArray(ease) + ? ease.map(motionUtils.easingDefinitionToFunction) + : motionUtils.easingDefinitionToFunction(ease); + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { + done: false, + value: keyframeValues[0], + }; + /** + * Create a times array based on the provided 0-1 offsets + */ + const absoluteTimes = convertOffsetToTimes( + // Only use the provided offsets if they're the correct length + // TODO Maybe we should warn here if there's a length mismatch + times && times.length === keyframeValues.length + ? times + : defaultOffset(keyframeValues), duration); + const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, { + ease: Array.isArray(easingFunctions) + ? easingFunctions + : defaultEasing(keyframeValues, easingFunctions), + }); + return { + calculatedDuration: duration, + next: (t) => { + state.value = mapTimeToKeyframe(t); + state.done = t >= duration; + return state; + }, + }; +} + +const isNotNull = (value) => value !== null; +function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1); + const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; +} + +const transitionTypeMap = { + decay: inertia, + inertia, + tween: keyframes, + keyframes: keyframes, + spring, +}; +function replaceTransitionType(transition) { + if (typeof transition.type === "string") { + transition.type = transitionTypeMap[transition.type]; + } +} + +class WithPromise { + constructor() { + this.updateFinished(); + } + get finished() { + return this._finished; + } + updateFinished() { + this._finished = new Promise((resolve) => { + this.resolve = resolve; + }); + } + notifyFinished() { + this.resolve(); + } + /** + * Allows the animation to be awaited. + * + * @deprecated Use `finished` instead. + */ + then(onResolve, onReject) { + return this.finished.then(onResolve, onReject); + } +} + +const percentToProgress = (percent) => percent / 100; +class JSAnimation extends WithPromise { + constructor(options) { + super(); + this.state = "idle"; + this.startTime = null; + this.isStopped = false; + /** + * The current time of the animation. + */ + this.currentTime = 0; + /** + * The time at which the animation was paused. + */ + this.holdTime = null; + /** + * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed. + */ + this.playbackSpeed = 1; + /** + * This method is bound to the instance to fix a pattern where + * animation.stop is returned as a reference from a useEffect. + */ + this.stop = () => { + const { motionValue } = this.options; + if (motionValue && motionValue.updatedAt !== time.now()) { + this.tick(time.now()); + } + this.isStopped = true; + if (this.state === "idle") + return; + this.teardown(); + this.options.onStop?.(); + }; + activeAnimations.mainThread++; + this.options = options; + this.initAnimation(); + this.play(); + if (options.autoplay === false) + this.pause(); + } + initAnimation() { + const { options } = this; + replaceTransitionType(options); + const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options; + let { keyframes: keyframes$1 } = options; + const generatorFactory = type || keyframes; + if (process.env.NODE_ENV !== "production" && + generatorFactory !== keyframes) { + motionUtils.invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`); + } + if (generatorFactory !== keyframes && + typeof keyframes$1[0] !== "number") { + this.mixKeyframes = motionUtils.pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1])); + keyframes$1 = [0, 100]; + } + const generator = generatorFactory({ ...options, keyframes: keyframes$1 }); + /** + * If we have a mirror repeat type we need to create a second generator that outputs the + * mirrored (not reversed) animation and later ping pong between the two generators. + */ + if (repeatType === "mirror") { + this.mirroredGenerator = generatorFactory({ + ...options, + keyframes: [...keyframes$1].reverse(), + velocity: -velocity, + }); + } + /** + * If duration is undefined and we have repeat options, + * we need to calculate a duration from the generator. + * + * We set it to the generator itself to cache the duration. + * Any timeline resolver will need to have already precalculated + * the duration by this step. + */ + if (generator.calculatedDuration === null) { + generator.calculatedDuration = calcGeneratorDuration(generator); + } + const { calculatedDuration } = generator; + this.calculatedDuration = calculatedDuration; + this.resolvedDuration = calculatedDuration + repeatDelay; + this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay; + this.generator = generator; + } + updateTime(timestamp) { + const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed; + // Update currentTime + if (this.holdTime !== null) { + this.currentTime = this.holdTime; + } + else { + // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 = + // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for + // example. + this.currentTime = animationTime; + } + } + tick(timestamp, sample = false) { + const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this; + if (this.startTime === null) + return generator.next(0); + const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options; + /** + * requestAnimationFrame timestamps can come through as lower than + * the startTime as set by performance.now(). Here we prevent this, + * though in the future it could be possible to make setting startTime + * a pending operation that gets resolved here. + */ + if (this.speed > 0) { + this.startTime = Math.min(this.startTime, timestamp); + } + else if (this.speed < 0) { + this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime); + } + if (sample) { + this.currentTime = timestamp; + } + else { + this.updateTime(timestamp); + } + // Rebase on delay + const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1); + const isInDelayPhase = this.playbackSpeed >= 0 + ? timeWithoutDelay < 0 + : timeWithoutDelay > totalDuration; + this.currentTime = Math.max(timeWithoutDelay, 0); + // If this animation has finished, set the current time to the total duration. + if (this.state === "finished" && this.holdTime === null) { + this.currentTime = totalDuration; + } + let elapsed = this.currentTime; + let frameGenerator = generator; + if (repeat) { + /** + * Get the current progress (0-1) of the animation. If t is > + * than duration we'll get values like 2.5 (midway through the + * third iteration) + */ + const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration; + /** + * Get the current iteration (0 indexed). For instance the floor of + * 2.5 is 2. + */ + let currentIteration = Math.floor(progress); + /** + * Get the current progress of the iteration by taking the remainder + * so 2.5 is 0.5 through iteration 2 + */ + let iterationProgress = progress % 1.0; + /** + * If iteration progress is 1 we count that as the end + * of the previous iteration. + */ + if (!iterationProgress && progress >= 1) { + iterationProgress = 1; + } + iterationProgress === 1 && currentIteration--; + currentIteration = Math.min(currentIteration, repeat + 1); + /** + * Reverse progress if we're not running in "normal" direction + */ + const isOddIteration = Boolean(currentIteration % 2); + if (isOddIteration) { + if (repeatType === "reverse") { + iterationProgress = 1 - iterationProgress; + if (repeatDelay) { + iterationProgress -= repeatDelay / resolvedDuration; + } + } + else if (repeatType === "mirror") { + frameGenerator = mirroredGenerator; + } + } + elapsed = motionUtils.clamp(0, 1, iterationProgress) * resolvedDuration; + } + /** + * If we're in negative time, set state as the initial keyframe. + * This prevents delay: x, duration: 0 animations from finishing + * instantly. + */ + const state = isInDelayPhase + ? { done: false, value: keyframes[0] } + : frameGenerator.next(elapsed); + if (mixKeyframes) { + state.value = mixKeyframes(state.value); + } + let { done } = state; + if (!isInDelayPhase && calculatedDuration !== null) { + done = + this.playbackSpeed >= 0 + ? this.currentTime >= totalDuration + : this.currentTime <= 0; + } + const isAnimationFinished = this.holdTime === null && + (this.state === "finished" || (this.state === "running" && done)); + // TODO: The exception for inertia could be cleaner here + if (isAnimationFinished && type !== inertia) { + state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed); + } + if (onUpdate) { + onUpdate(state.value); + } + if (isAnimationFinished) { + this.finish(); + } + return state; + } + /** + * Allows the returned animation to be awaited or promise-chained. Currently + * resolves when the animation finishes at all but in a future update could/should + * reject if its cancels. + */ + then(resolve, reject) { + return this.finished.then(resolve, reject); + } + get duration() { + return motionUtils.millisecondsToSeconds(this.calculatedDuration); + } + get time() { + return motionUtils.millisecondsToSeconds(this.currentTime); + } + set time(newTime) { + newTime = motionUtils.secondsToMilliseconds(newTime); + this.currentTime = newTime; + if (this.startTime === null || + this.holdTime !== null || + this.playbackSpeed === 0) { + this.holdTime = newTime; + } + else if (this.driver) { + this.startTime = this.driver.now() - newTime / this.playbackSpeed; + } + this.driver?.start(false); + } + get speed() { + return this.playbackSpeed; + } + set speed(newSpeed) { + this.updateTime(time.now()); + const hasChanged = this.playbackSpeed !== newSpeed; + this.playbackSpeed = newSpeed; + if (hasChanged) { + this.time = motionUtils.millisecondsToSeconds(this.currentTime); + } + } + play() { + if (this.isStopped) + return; + const { driver = frameloopDriver, startTime } = this.options; + if (!this.driver) { + this.driver = driver((timestamp) => this.tick(timestamp)); + } + this.options.onPlay?.(); + const now = this.driver.now(); + if (this.state === "finished") { + this.updateFinished(); + this.startTime = now; + } + else if (this.holdTime !== null) { + this.startTime = now - this.holdTime; + } + else if (!this.startTime) { + this.startTime = startTime ?? now; + } + if (this.state === "finished" && this.speed < 0) { + this.startTime += this.calculatedDuration; + } + this.holdTime = null; + /** + * Set playState to running only after we've used it in + * the previous logic. + */ + this.state = "running"; + this.driver.start(); + } + pause() { + this.state = "paused"; + this.updateTime(time.now()); + this.holdTime = this.currentTime; + } + complete() { + if (this.state !== "running") { + this.play(); + } + this.state = "finished"; + this.holdTime = null; + } + finish() { + this.notifyFinished(); + this.teardown(); + this.state = "finished"; + this.options.onComplete?.(); + } + cancel() { + this.holdTime = null; + this.startTime = 0; + this.tick(0); + this.teardown(); + this.options.onCancel?.(); + } + teardown() { + this.state = "idle"; + this.stopDriver(); + this.startTime = this.holdTime = null; + activeAnimations.mainThread--; + } + stopDriver() { + if (!this.driver) + return; + this.driver.stop(); + this.driver = undefined; + } + sample(sampleTime) { + this.startTime = 0; + return this.tick(sampleTime, true); + } + attachTimeline(timeline) { + if (this.options.allowFlatten) { + this.options.type = "keyframes"; + this.options.ease = "linear"; + this.initAnimation(); + } + this.driver?.stop(); + return timeline.observe(this); + } +} +// Legacy function support +function animateValue(options) { + return new JSAnimation(options); +} + +function fillWildcards(keyframes) { + for (let i = 1; i < keyframes.length; i++) { + keyframes[i] ?? (keyframes[i] = keyframes[i - 1]); + } +} + +const radToDeg = (rad) => (rad * 180) / Math.PI; +const rotate = (v) => { + const angle = radToDeg(Math.atan2(v[1], v[0])); + return rebaseAngle(angle); +}; +const matrix2dParsers = { + x: 4, + y: 5, + translateX: 4, + translateY: 5, + scaleX: 0, + scaleY: 3, + scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2, + rotate, + rotateZ: rotate, + skewX: (v) => radToDeg(Math.atan(v[1])), + skewY: (v) => radToDeg(Math.atan(v[2])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2, +}; +const rebaseAngle = (angle) => { + angle = angle % 360; + if (angle < 0) + angle += 360; + return angle; +}; +const rotateZ = rotate; +const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]); +const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]); +const matrix3dParsers = { + x: 12, + y: 13, + z: 14, + translateX: 12, + translateY: 13, + translateZ: 14, + scaleX, + scaleY, + scale: (v) => (scaleX(v) + scaleY(v)) / 2, + rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))), + rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))), + rotateZ, + rotate: rotateZ, + skewX: (v) => radToDeg(Math.atan(v[4])), + skewY: (v) => radToDeg(Math.atan(v[1])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2, +}; +function defaultTransformValue(name) { + return name.includes("scale") ? 1 : 0; +} +function parseValueFromTransform(transform, name) { + if (!transform || transform === "none") { + return defaultTransformValue(name); + } + const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u); + let parsers; + let match; + if (matrix3dMatch) { + parsers = matrix3dParsers; + match = matrix3dMatch; + } + else { + const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u); + parsers = matrix2dParsers; + match = matrix2dMatch; + } + if (!match) { + return defaultTransformValue(name); + } + const valueParser = parsers[name]; + const values = match[1].split(",").map(convertTransformToNumber); + return typeof valueParser === "function" + ? valueParser(values) + : values[valueParser]; +} +const readTransformValue = (instance, name) => { + const { transform = "none" } = getComputedStyle(instance); + return parseValueFromTransform(transform, name); +}; +function convertTransformToNumber(value) { + return parseFloat(value.trim()); +} + +/** + * Generate a list of every possible transform key. + */ +const transformPropOrder = [ + "transformPerspective", + "x", + "y", + "z", + "translateX", + "translateY", + "translateZ", + "scale", + "scaleX", + "scaleY", + "rotate", + "rotateX", + "rotateY", + "rotateZ", + "skew", + "skewX", + "skewY", +]; +/** + * A quick lookup for transform props. + */ +const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))(); + +const isNumOrPxType = (v) => v === number || v === px; +const transformKeys = new Set(["x", "y", "z"]); +const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key)); +function removeNonTranslationalTransform(visualElement) { + const removedTransforms = []; + nonTranslationalTransformKeys.forEach((key) => { + const value = visualElement.getValue(key); + if (value !== undefined) { + removedTransforms.push([key, value.get()]); + value.set(key.startsWith("scale") ? 1 : 0); + } + }); + return removedTransforms; +} +const positionalValues = { + // Dimensions + width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight), + height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom), + top: (_bbox, { top }) => parseFloat(top), + left: (_bbox, { left }) => parseFloat(left), + bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min), + right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min), + // Transform + x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"), + y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"), +}; +// Alias translate longform names +positionalValues.translateX = positionalValues.x; +positionalValues.translateY = positionalValues.y; + +const toResolve = new Set(); +let isScheduled = false; +let anyNeedsMeasurement = false; +let isForced = false; +function measureAllKeyframes() { + if (anyNeedsMeasurement) { + const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement); + const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element)); + const transformsToRestore = new Map(); + /** + * Write pass + * If we're measuring elements we want to remove bounding box-changing transforms. + */ + elementsToMeasure.forEach((element) => { + const removedTransforms = removeNonTranslationalTransform(element); + if (!removedTransforms.length) + return; + transformsToRestore.set(element, removedTransforms); + element.render(); + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureInitialState()); + // Write + elementsToMeasure.forEach((element) => { + element.render(); + const restore = transformsToRestore.get(element); + if (restore) { + restore.forEach(([key, value]) => { + element.getValue(key)?.set(value); + }); + } + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureEndState()); + // Write + resolversToMeasure.forEach((resolver) => { + if (resolver.suspendedScrollY !== undefined) { + window.scrollTo(0, resolver.suspendedScrollY); + } + }); + } + anyNeedsMeasurement = false; + isScheduled = false; + toResolve.forEach((resolver) => resolver.complete(isForced)); + toResolve.clear(); +} +function readAllKeyframes() { + toResolve.forEach((resolver) => { + resolver.readKeyframes(); + if (resolver.needsMeasurement) { + anyNeedsMeasurement = true; + } + }); +} +function flushKeyframeResolvers() { + isForced = true; + readAllKeyframes(); + measureAllKeyframes(); + isForced = false; +} +class KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) { + this.state = "pending"; + /** + * Track whether this resolver is async. If it is, it'll be added to the + * resolver queue and flushed in the next frame. Resolvers that aren't going + * to trigger read/write thrashing don't need to be async. + */ + this.isAsync = false; + /** + * Track whether this resolver needs to perform a measurement + * to resolve its keyframes. + */ + this.needsMeasurement = false; + this.unresolvedKeyframes = [...unresolvedKeyframes]; + this.onComplete = onComplete; + this.name = name; + this.motionValue = motionValue; + this.element = element; + this.isAsync = isAsync; + } + scheduleResolve() { + this.state = "scheduled"; + if (this.isAsync) { + toResolve.add(this); + if (!isScheduled) { + isScheduled = true; + frame.read(readAllKeyframes); + frame.resolveKeyframes(measureAllKeyframes); + } + } + else { + this.readKeyframes(); + this.complete(); + } + } + readKeyframes() { + const { unresolvedKeyframes, name, element, motionValue } = this; + // If initial keyframe is null we need to read it from the DOM + if (unresolvedKeyframes[0] === null) { + const currentValue = motionValue?.get(); + // TODO: This doesn't work if the final keyframe is a wildcard + const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (currentValue !== undefined) { + unresolvedKeyframes[0] = currentValue; + } + else if (element && name) { + const valueAsRead = element.readValue(name, finalKeyframe); + if (valueAsRead !== undefined && valueAsRead !== null) { + unresolvedKeyframes[0] = valueAsRead; + } + } + if (unresolvedKeyframes[0] === undefined) { + unresolvedKeyframes[0] = finalKeyframe; + } + if (motionValue && currentValue === undefined) { + motionValue.set(unresolvedKeyframes[0]); + } + } + fillWildcards(unresolvedKeyframes); + } + setFinalKeyframe() { } + measureInitialState() { } + renderEndStyles() { } + measureEndState() { } + complete(isForcedComplete = false) { + this.state = "complete"; + this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForcedComplete); + toResolve.delete(this); + } + cancel() { + if (this.state === "scheduled") { + toResolve.delete(this); + this.state = "pending"; + } + } + resume() { + if (this.state === "pending") + this.scheduleResolve(); + } +} + +const isCSSVar = (name) => name.startsWith("--"); + +function setStyle(element, name, value) { + isCSSVar(name) + ? element.style.setProperty(name, value) + : (element.style[name] = value); +} + +const supportsScrollTimeline = /* @__PURE__ */ motionUtils.memo(() => window.ScrollTimeline !== undefined); + +/** + * Add the ability for test suites to manually set support flags + * to better test more environments. + */ +const supportsFlags = {}; + +function memoSupports(callback, supportsFlag) { + const memoized = motionUtils.memo(callback); + return () => supportsFlags[supportsFlag] ?? memoized(); +} + +const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => { + try { + document + .createElement("div") + .animate({ opacity: 0 }, { easing: "linear(0, 1)" }); + } + catch (e) { + return false; + } + return true; +}, "linearEasing"); + +const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`; + +const supportedWaapiEasing = { + linear: "linear", + ease: "ease", + easeIn: "ease-in", + easeOut: "ease-out", + easeInOut: "ease-in-out", + circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]), + circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]), + backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]), + backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]), +}; + +function mapEasingToNativeEasing(easing, duration) { + if (!easing) { + return undefined; + } + else if (typeof easing === "function") { + return supportsLinearEasing() + ? generateLinearEasing(easing, duration) + : "ease-out"; + } + else if (motionUtils.isBezierDefinition(easing)) { + return cubicBezierAsString(easing); + } + else if (Array.isArray(easing)) { + return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) || + supportedWaapiEasing.easeOut); + } + else { + return supportedWaapiEasing[easing]; + } +} + +function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) { + const keyframeOptions = { + [valueName]: keyframes, + }; + if (times) + keyframeOptions.offset = times; + const easing = mapEasingToNativeEasing(ease, duration); + /** + * If this is an easing array, apply to keyframes, not animation as a whole + */ + if (Array.isArray(easing)) + keyframeOptions.easing = easing; + if (statsBuffer.value) { + activeAnimations.waapi++; + } + const options = { + delay, + duration, + easing: !Array.isArray(easing) ? easing : "linear", + fill: "both", + iterations: repeat + 1, + direction: repeatType === "reverse" ? "alternate" : "normal", + }; + if (pseudoElement) + options.pseudoElement = pseudoElement; + const animation = element.animate(keyframeOptions, options); + if (statsBuffer.value) { + animation.finished.finally(() => { + activeAnimations.waapi--; + }); + } + return animation; +} + +function isGenerator(type) { + return typeof type === "function" && "applyToOptions" in type; +} + +function applyGeneratorOptions({ type, ...options }) { + if (isGenerator(type) && supportsLinearEasing()) { + return type.applyToOptions(options); + } + else { + options.duration ?? (options.duration = 300); + options.ease ?? (options.ease = "easeOut"); + } + return options; +} + +/** + * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API. + */ +class NativeAnimation extends WithPromise { + constructor(options) { + super(); + this.finishedTime = null; + this.isStopped = false; + if (!options) + return; + const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options; + this.isPseudoElement = Boolean(pseudoElement); + this.allowFlatten = allowFlatten; + this.options = options; + motionUtils.invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`); + const transition = applyGeneratorOptions(options); + this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement); + if (transition.autoplay === false) { + this.animation.pause(); + } + this.animation.onfinish = () => { + this.finishedTime = this.time; + if (!pseudoElement) { + const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed); + if (this.updateMotionValue) { + this.updateMotionValue(keyframe); + } + else { + /** + * If we can, we want to commit the final style as set by the user, + * rather than the computed keyframe value supplied by the animation. + */ + setStyle(element, name, keyframe); + } + this.animation.cancel(); + } + onComplete?.(); + this.notifyFinished(); + }; + } + play() { + if (this.isStopped) + return; + this.animation.play(); + if (this.state === "finished") { + this.updateFinished(); + } + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.finish?.(); + } + cancel() { + try { + this.animation.cancel(); + } + catch (e) { } + } + stop() { + if (this.isStopped) + return; + this.isStopped = true; + const { state } = this; + if (state === "idle" || state === "finished") { + return; + } + if (this.updateMotionValue) { + this.updateMotionValue(); + } + else { + this.commitStyles(); + } + if (!this.isPseudoElement) + this.cancel(); + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * In this method, we commit styles back to the DOM before cancelling + * the animation. + * + * This is designed to be overridden by NativeAnimationExtended, which + * will create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to also correctly calculate velocity for any subsequent animation + * while deferring the commit until the next animation frame. + */ + commitStyles() { + if (!this.isPseudoElement) { + this.animation.commitStyles?.(); + } + } + get duration() { + const duration = this.animation.effect?.getComputedTiming?.().duration || 0; + return motionUtils.millisecondsToSeconds(Number(duration)); + } + get time() { + return motionUtils.millisecondsToSeconds(Number(this.animation.currentTime) || 0); + } + set time(newTime) { + this.finishedTime = null; + this.animation.currentTime = motionUtils.secondsToMilliseconds(newTime); + } + /** + * The playback speed of the animation. + * 1 = normal speed, 2 = double speed, 0.5 = half speed. + */ + get speed() { + return this.animation.playbackRate; + } + set speed(newSpeed) { + // Allow backwards playback after finishing + if (newSpeed < 0) + this.finishedTime = null; + this.animation.playbackRate = newSpeed; + } + get state() { + return this.finishedTime !== null + ? "finished" + : this.animation.playState; + } + get startTime() { + return Number(this.animation.startTime); + } + set startTime(newStartTime) { + this.animation.startTime = newStartTime; + } + /** + * Attaches a timeline to the animation, for instance the `ScrollTimeline`. + */ + attachTimeline({ timeline, observe }) { + if (this.allowFlatten) { + this.animation.effect?.updateTiming({ easing: "linear" }); + } + this.animation.onfinish = null; + if (timeline && supportsScrollTimeline()) { + this.animation.timeline = timeline; + return motionUtils.noop; + } + else { + return observe(this); + } + } +} + +const unsupportedEasingFunctions = { + anticipate: motionUtils.anticipate, + backInOut: motionUtils.backInOut, + circInOut: motionUtils.circInOut, +}; +function isUnsupportedEase(key) { + return key in unsupportedEasingFunctions; +} +function replaceStringEasing(transition) { + if (typeof transition.ease === "string" && + isUnsupportedEase(transition.ease)) { + transition.ease = unsupportedEasingFunctions[transition.ease]; + } +} + +/** + * 10ms is chosen here as it strikes a balance between smooth + * results (more than one keyframe per frame at 60fps) and + * keyframe quantity. + */ +const sampleDelta = 10; //ms +class NativeAnimationExtended extends NativeAnimation { + constructor(options) { + /** + * The base NativeAnimation function only supports a subset + * of Motion easings, and WAAPI also only supports some + * easing functions via string/cubic-bezier definitions. + * + * This function replaces those unsupported easing functions + * with a JS easing function. This will later get compiled + * to a linear() easing function. + */ + replaceStringEasing(options); + /** + * Ensure we replace the transition type with a generator function + * before passing to WAAPI. + * + * TODO: Does this have a better home? It could be shared with + * JSAnimation. + */ + replaceTransitionType(options); + super(options); + if (options.startTime) { + this.startTime = options.startTime; + } + this.options = options; + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * Rather than read commited styles back out of the DOM, we can + * create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to calculate velocity for any subsequent animation. + */ + updateMotionValue(value) { + const { motionValue, onUpdate, onComplete, element, ...options } = this.options; + if (!motionValue) + return; + if (value !== undefined) { + motionValue.set(value); + return; + } + const sampleAnimation = new JSAnimation({ + ...options, + autoplay: false, + }); + const sampleTime = motionUtils.secondsToMilliseconds(this.finishedTime ?? this.time); + motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta); + sampleAnimation.stop(); + } +} + +/** + * Check if a value is animatable. Examples: + * + * ✅: 100, "100px", "#fff" + * ❌: "block", "url(2.jpg)" + * @param value + * + * @internal + */ +const isAnimatable = (value, name) => { + // If the list of keys tat might be non-animatable grows, replace with Set + if (name === "zIndex") + return false; + // If it's a number or a keyframes array, we can animate it. We might at some point + // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this, + // but for now lets leave it like this for performance reasons + if (typeof value === "number" || Array.isArray(value)) + return true; + if (typeof value === "string" && // It's animatable if we have a string + (complex.test(value) || value === "0") && // And it contains numbers and/or colors + !value.startsWith("url(") // Unless it starts with "url(" + ) { + return true; + } + return false; +}; + +function hasKeyframesChanged(keyframes) { + const current = keyframes[0]; + if (keyframes.length === 1) + return true; + for (let i = 0; i < keyframes.length; i++) { + if (keyframes[i] !== current) + return true; + } +} +function canAnimate(keyframes, name, type, velocity) { + /** + * Check if we're able to animate between the start and end keyframes, + * and throw a warning if we're attempting to animate between one that's + * animatable and another that isn't. + */ + const originKeyframe = keyframes[0]; + if (originKeyframe === null) + return false; + /** + * These aren't traditionally animatable but we do support them. + * In future we could look into making this more generic or replacing + * this function with mix() === mixImmediate + */ + if (name === "display" || name === "visibility") + return true; + const targetKeyframe = keyframes[keyframes.length - 1]; + const isOriginAnimatable = isAnimatable(originKeyframe, name); + const isTargetAnimatable = isAnimatable(targetKeyframe, name); + motionUtils.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`); + // Always skip if any of these are true + if (!isOriginAnimatable || !isTargetAnimatable) { + return false; + } + return (hasKeyframesChanged(keyframes) || + ((type === "spring" || isGenerator(type)) && velocity)); +} + +/** + * Checks if an element is an HTML element in a way + * that works across iframes + */ +function isHTMLElement(element) { + return motionUtils.isObject(element) && "offsetHeight" in element; +} + +/** + * A list of values that can be hardware-accelerated. + */ +const acceleratedValues$1 = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Could be re-enabled now we have support for linear() easing + // "background-color" +]); +const supportsWaapi = /*@__PURE__*/ motionUtils.memo(() => Object.hasOwnProperty.call(Element.prototype, "animate")); +function supportsBrowserAnimation(options) { + const { motionValue, name, repeatDelay, repeatType, damping, type } = options; + if (!isHTMLElement(motionValue?.owner?.current)) { + return false; + } + const { onUpdate, transformTemplate } = motionValue.owner.getProps(); + return (supportsWaapi() && + name && + acceleratedValues$1.has(name) && + (name !== "transform" || !transformTemplate) && + /** + * If we're outputting values to onUpdate then we can't use WAAPI as there's + * no way to read the value from WAAPI every frame. + */ + !onUpdate && + !repeatDelay && + repeatType !== "mirror" && + damping !== 0 && + type !== "inertia"); +} + +/** + * Maximum time allowed between an animation being created and it being + * resolved for us to use the latter as the start time. + * + * This is to ensure that while we prefer to "start" an animation as soon + * as it's triggered, we also want to avoid a visual jump if there's a big delay + * between these two moments. + */ +const MAX_RESOLVE_DELAY = 40; +class AsyncMotionValueAnimation extends WithPromise { + constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) { + super(); + /** + * Bound to support return animation.stop pattern + */ + this.stop = () => { + if (this._animation) { + this._animation.stop(); + this.stopTimeline?.(); + } + this.keyframeResolver?.cancel(); + }; + this.createdAt = time.now(); + const optionsWithDefaults = { + autoplay, + delay, + type, + repeat, + repeatDelay, + repeatType, + name, + motionValue, + element, + ...options, + }; + const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver; + this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element); + this.keyframeResolver?.scheduleResolve(); + } + onKeyframesResolved(keyframes, finalKeyframe, options, sync) { + this.keyframeResolver = undefined; + const { name, type, velocity, delay, isHandoff, onUpdate } = options; + this.resolvedAt = time.now(); + /** + * If we can't animate this value with the resolved keyframes + * then we should complete it immediately. + */ + if (!canAnimate(keyframes, name, type, velocity)) { + if (motionUtils.MotionGlobalConfig.instantAnimations || !delay) { + onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe)); + } + keyframes[0] = keyframes[keyframes.length - 1]; + options.duration = 0; + options.repeat = 0; + } + /** + * Resolve startTime for the animation. + * + * This method uses the createdAt and resolvedAt to calculate the + * animation startTime. *Ideally*, we would use the createdAt time as t=0 + * as the following frame would then be the first frame of the animation in + * progress, which would feel snappier. + * + * However, if there's a delay (main thread work) between the creation of + * the animation and the first commited frame, we prefer to use resolvedAt + * to avoid a sudden jump into the animation. + */ + const startTime = sync + ? !this.resolvedAt + ? this.createdAt + : this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY + ? this.resolvedAt + : this.createdAt + : undefined; + const resolvedOptions = { + startTime, + finalKeyframe, + ...options, + keyframes, + }; + /** + * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via + * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the + * optimised animation. + */ + const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions) + ? new NativeAnimationExtended({ + ...resolvedOptions, + element: resolvedOptions.motionValue.owner.current, + }) + : new JSAnimation(resolvedOptions); + animation.finished.then(() => this.notifyFinished()).catch(motionUtils.noop); + if (this.pendingTimeline) { + this.stopTimeline = animation.attachTimeline(this.pendingTimeline); + this.pendingTimeline = undefined; + } + this._animation = animation; + } + get finished() { + if (!this._animation) { + return this._finished; + } + else { + return this.animation.finished; + } + } + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } + get animation() { + if (!this._animation) { + this.keyframeResolver?.resume(); + flushKeyframeResolvers(); + } + return this._animation; + } + get duration() { + return this.animation.duration; + } + get time() { + return this.animation.time; + } + set time(newTime) { + this.animation.time = newTime; + } + get speed() { + return this.animation.speed; + } + get state() { + return this.animation.state; + } + set speed(newSpeed) { + this.animation.speed = newSpeed; + } + get startTime() { + return this.animation.startTime; + } + attachTimeline(timeline) { + if (this._animation) { + this.stopTimeline = this.animation.attachTimeline(timeline); + } + else { + this.pendingTimeline = timeline; + } + return () => this.stop(); + } + play() { + this.animation.play(); + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.complete(); + } + cancel() { + if (this._animation) { + this.animation.cancel(); + } + this.keyframeResolver?.cancel(); + } +} + +class GroupAnimation { + constructor(animations) { + // Bound to accomadate common `return animation.stop` pattern + this.stop = () => this.runAll("stop"); + this.animations = animations.filter(Boolean); + } + get finished() { + return Promise.all(this.animations.map((animation) => animation.finished)); + } + /** + * TODO: Filter out cancelled or stopped animations before returning + */ + getAll(propName) { + return this.animations[0][propName]; + } + setAll(propName, newValue) { + for (let i = 0; i < this.animations.length; i++) { + this.animations[i][propName] = newValue; + } + } + attachTimeline(timeline) { + const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline)); + return () => { + subscriptions.forEach((cancel, i) => { + cancel && cancel(); + this.animations[i].stop(); + }); + }; + } + get time() { + return this.getAll("time"); + } + set time(time) { + this.setAll("time", time); + } + get speed() { + return this.getAll("speed"); + } + set speed(speed) { + this.setAll("speed", speed); + } + get state() { + return this.getAll("state"); + } + get startTime() { + return this.getAll("startTime"); + } + get duration() { + let max = 0; + for (let i = 0; i < this.animations.length; i++) { + max = Math.max(max, this.animations[i].duration); + } + return max; + } + runAll(methodName) { + this.animations.forEach((controls) => controls[methodName]()); + } + play() { + this.runAll("play"); + } + pause() { + this.runAll("pause"); + } + cancel() { + this.runAll("cancel"); + } + complete() { + this.runAll("complete"); + } +} + +class GroupAnimationWithThen extends GroupAnimation { + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } +} + +class NativeAnimationWrapper extends NativeAnimation { + constructor(animation) { + super(); + this.animation = animation; + animation.onfinish = () => { + this.finishedTime = this.time; + this.notifyFinished(); + }; + } +} + +const animationMaps = new WeakMap(); +const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`; +function getAnimationMap(element) { + const map = animationMaps.get(element) || new Map(); + animationMaps.set(element, map); + return map; +} + +/** + * Parse Framer's special CSS variable format into a CSS token and a fallback. + * + * ``` + * `var(--foo, #fff)` => [`--foo`, '#fff'] + * ``` + * + * @param current + */ +const splitCSSVariableRegex = +// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words +/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u; +function parseCSSVariable(current) { + const match = splitCSSVariableRegex.exec(current); + if (!match) + return [,]; + const [, token1, token2, fallback] = match; + return [`--${token1 ?? token2}`, fallback]; +} +const maxDepth = 4; +function getVariableValue(current, element, depth = 1) { + motionUtils.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`); + const [token, fallback] = parseCSSVariable(current); + // No CSS variable detected + if (!token) + return; + // Attempt to read this CSS variable off the element + const resolved = window.getComputedStyle(element).getPropertyValue(token); + if (resolved) { + const trimmed = resolved.trim(); + return motionUtils.isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed; + } + return isCSSVariableToken(fallback) + ? getVariableValue(fallback, element, depth + 1) + : fallback; +} + +function getValueTransition(transition, key) { + return (transition?.[key] ?? + transition?.["default"] ?? + transition); +} + +const positionalKeys = new Set([ + "width", + "height", + "top", + "left", + "right", + "bottom", + ...transformPropOrder, +]); + +/** + * ValueType for "auto" + */ +const auto = { + test: (v) => v === "auto", + parse: (v) => v, +}; + +/** + * Tests a provided value against a ValueType + */ +const testValueType = (v) => (type) => type.test(v); + +/** + * A list of value types commonly used for dimensions + */ +const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto]; +/** + * Tests a dimensional value against the list of dimension ValueTypes + */ +const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v)); + +function isNone(value) { + if (typeof value === "number") { + return value === 0; + } + else if (value !== null) { + return value === "none" || value === "0" || motionUtils.isZeroValueString(value); + } + else { + return true; + } +} + +/** + * Properties that should default to 1 or 100% + */ +const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]); +function applyDefaultFilter(v) { + const [name, value] = v.slice(0, -1).split("("); + if (name === "drop-shadow") + return v; + const [number] = value.match(floatRegex) || []; + if (!number) + return v; + const unit = value.replace(number, ""); + let defaultValue = maxDefaults.has(name) ? 1 : 0; + if (number !== value) + defaultValue *= 100; + return name + "(" + defaultValue + unit + ")"; +} +const functionRegex = /\b([a-z-]*)\(.*?\)/gu; +const filter = { + ...complex, + getAnimatableNone: (v) => { + const functions = v.match(functionRegex); + return functions ? functions.map(applyDefaultFilter).join(" ") : v; + }, +}; + +const int = { + ...number, + transform: Math.round, +}; + +const transformValueTypes = { + rotate: degrees, + rotateX: degrees, + rotateY: degrees, + rotateZ: degrees, + scale, + scaleX: scale, + scaleY: scale, + scaleZ: scale, + skew: degrees, + skewX: degrees, + skewY: degrees, + distance: px, + translateX: px, + translateY: px, + translateZ: px, + x: px, + y: px, + z: px, + perspective: px, + transformPerspective: px, + opacity: alpha, + originX: progressPercentage, + originY: progressPercentage, + originZ: px, +}; + +const numberValueTypes = { + // Border props + borderWidth: px, + borderTopWidth: px, + borderRightWidth: px, + borderBottomWidth: px, + borderLeftWidth: px, + borderRadius: px, + radius: px, + borderTopLeftRadius: px, + borderTopRightRadius: px, + borderBottomRightRadius: px, + borderBottomLeftRadius: px, + // Positioning props + width: px, + maxWidth: px, + height: px, + maxHeight: px, + top: px, + right: px, + bottom: px, + left: px, + // Spacing props + padding: px, + paddingTop: px, + paddingRight: px, + paddingBottom: px, + paddingLeft: px, + margin: px, + marginTop: px, + marginRight: px, + marginBottom: px, + marginLeft: px, + // Misc + backgroundPositionX: px, + backgroundPositionY: px, + ...transformValueTypes, + zIndex: int, + // SVG + fillOpacity: alpha, + strokeOpacity: alpha, + numOctaves: int, +}; + +/** + * A map of default value types for common values + */ +const defaultValueTypes = { + ...numberValueTypes, + // Color props + color, + backgroundColor: color, + outlineColor: color, + fill: color, + stroke: color, + // Border props + borderColor: color, + borderTopColor: color, + borderRightColor: color, + borderBottomColor: color, + borderLeftColor: color, + filter, + WebkitFilter: filter, +}; +/** + * Gets the default ValueType for the provided value key + */ +const getDefaultValueType = (key) => defaultValueTypes[key]; + +function getAnimatableNone(key, value) { + let defaultValueType = getDefaultValueType(key); + if (defaultValueType !== filter) + defaultValueType = complex; + // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target + return defaultValueType.getAnimatableNone + ? defaultValueType.getAnimatableNone(value) + : undefined; +} + +/** + * If we encounter keyframes like "none" or "0" and we also have keyframes like + * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for + * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into + * zero equivalents, i.e. "#fff0" or "0px 0px". + */ +const invalidTemplates = new Set(["auto", "none", "0"]); +function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) { + let i = 0; + let animatableTemplate = undefined; + while (i < unresolvedKeyframes.length && !animatableTemplate) { + const keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string" && + !invalidTemplates.has(keyframe) && + analyseComplexValue(keyframe).values.length) { + animatableTemplate = unresolvedKeyframes[i]; + } + i++; + } + if (animatableTemplate && name) { + for (const noneIndex of noneKeyframeIndexes) { + unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate); + } + } +} + +class DOMKeyframesResolver extends KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element) { + super(unresolvedKeyframes, onComplete, name, motionValue, element, true); + } + readKeyframes() { + const { unresolvedKeyframes, element, name } = this; + if (!element || !element.current) + return; + super.readKeyframes(); + /** + * If any keyframe is a CSS variable, we need to find its value by sampling the element + */ + for (let i = 0; i < unresolvedKeyframes.length; i++) { + let keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string") { + keyframe = keyframe.trim(); + if (isCSSVariableToken(keyframe)) { + const resolved = getVariableValue(keyframe, element.current); + if (resolved !== undefined) { + unresolvedKeyframes[i] = resolved; + } + if (i === unresolvedKeyframes.length - 1) { + this.finalKeyframe = keyframe; + } + } + } + } + /** + * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes. + * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which + * have a far bigger performance impact. + */ + this.resolveNoneKeyframes(); + /** + * Check to see if unit type has changed. If so schedule jobs that will + * temporarily set styles to the destination keyframes. + * Skip if we have more than two keyframes or this isn't a positional value. + * TODO: We can throw if there are multiple keyframes and the value type changes. + */ + if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) { + return; + } + const [origin, target] = unresolvedKeyframes; + const originType = findDimensionValueType(origin); + const targetType = findDimensionValueType(target); + /** + * Either we don't recognise these value types or we can animate between them. + */ + if (originType === targetType) + return; + /** + * If both values are numbers or pixels, we can animate between them by + * converting them to numbers. + */ + if (isNumOrPxType(originType) && isNumOrPxType(targetType)) { + for (let i = 0; i < unresolvedKeyframes.length; i++) { + const value = unresolvedKeyframes[i]; + if (typeof value === "string") { + unresolvedKeyframes[i] = parseFloat(value); + } + } + } + else if (positionalValues[name]) { + /** + * Else, the only way to resolve this is by measuring the element. + */ + this.needsMeasurement = true; + } + } + resolveNoneKeyframes() { + const { unresolvedKeyframes, name } = this; + const noneKeyframeIndexes = []; + for (let i = 0; i < unresolvedKeyframes.length; i++) { + if (unresolvedKeyframes[i] === null || + isNone(unresolvedKeyframes[i])) { + noneKeyframeIndexes.push(i); + } + } + if (noneKeyframeIndexes.length) { + makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name); + } + } + measureInitialState() { + const { element, unresolvedKeyframes, name } = this; + if (!element || !element.current) + return; + if (name === "height") { + this.suspendedScrollY = window.pageYOffset; + } + this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + unresolvedKeyframes[0] = this.measuredOrigin; + // Set final key frame to measure after next render + const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (measureKeyframe !== undefined) { + element.getValue(name, measureKeyframe).jump(measureKeyframe, false); + } + } + measureEndState() { + const { element, name, unresolvedKeyframes } = this; + if (!element || !element.current) + return; + const value = element.getValue(name); + value && value.jump(this.measuredOrigin, false); + const finalKeyframeIndex = unresolvedKeyframes.length - 1; + const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex]; + unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + if (finalKeyframe !== null && this.finalKeyframe === undefined) { + this.finalKeyframe = finalKeyframe; + } + // If we removed transform values, reapply them before the next render + if (this.removedTransforms?.length) { + this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => { + element + .getValue(unsetTransformName) + .set(unsetTransformValue); + }); + } + this.resolveNoneKeyframes(); + } +} + +const pxValues = new Set([ + // Border props + "borderWidth", + "borderTopWidth", + "borderRightWidth", + "borderBottomWidth", + "borderLeftWidth", + "borderRadius", + "radius", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + // Positioning props + "width", + "maxWidth", + "height", + "maxHeight", + "top", + "right", + "bottom", + "left", + // Spacing props + "padding", + "paddingTop", + "paddingRight", + "paddingBottom", + "paddingLeft", + "margin", + "marginTop", + "marginRight", + "marginBottom", + "marginLeft", + // Misc + "backgroundPositionX", + "backgroundPositionY", +]); + +function applyPxDefaults(keyframes, name) { + for (let i = 0; i < keyframes.length; i++) { + if (typeof keyframes[i] === "number" && pxValues.has(name)) { + keyframes[i] = keyframes[i] + "px"; + } + } +} + +function isWaapiSupportedEasing(easing) { + return Boolean((typeof easing === "function" && supportsLinearEasing()) || + !easing || + (typeof easing === "string" && + (easing in supportedWaapiEasing || supportsLinearEasing())) || + motionUtils.isBezierDefinition(easing) || + (Array.isArray(easing) && easing.every(isWaapiSupportedEasing))); +} + +const supportsPartialKeyframes = /*@__PURE__*/ motionUtils.memo(() => { + try { + document.createElement("div").animate({ opacity: [1] }); + } + catch (e) { + return false; + } + return true; +}); + +/** + * A list of values that can be hardware-accelerated. + */ +const acceleratedValues = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved + // or until we implement support for linear() easing. + // "background-color" +]); + +function camelToDash(str) { + return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`); +} + +function resolveElements(elementOrSelector, scope, selectorCache) { + if (elementOrSelector instanceof EventTarget) { + return [elementOrSelector]; + } + else if (typeof elementOrSelector === "string") { + let root = document; + if (scope) { + root = scope.current; + } + const elements = selectorCache?.[elementOrSelector] ?? + root.querySelectorAll(elementOrSelector); + return elements ? Array.from(elements) : []; + } + return Array.from(elementOrSelector); +} + +function createSelectorEffect(subjectEffect) { + return (subject, values) => { + const elements = resolveElements(subject); + const subscriptions = []; + for (const element of elements) { + const remove = subjectEffect(element, values); + subscriptions.push(remove); + } + return () => { + for (const remove of subscriptions) + remove(); + }; + }; +} + +/** + * Provided a value and a ValueType, returns the value as that value type. + */ +const getValueAsType = (value, type) => { + return type && typeof value === "number" + ? type.transform(value) + : value; +}; + +class MotionValueState { + constructor() { + this.latest = {}; + this.values = new Map(); + } + set(name, value, render, computed, useDefaultValueType = true) { + const existingValue = this.values.get(name); + if (existingValue) { + existingValue.onRemove(); + } + const onChange = () => { + const v = value.get(); + if (useDefaultValueType) { + this.latest[name] = getValueAsType(v, numberValueTypes[name]); + } + else { + this.latest[name] = v; + } + render && frame.render(render); + }; + onChange(); + const cancelOnChange = value.on("change", onChange); + computed && value.addDependent(computed); + const remove = () => { + cancelOnChange(); + render && cancelFrame(render); + this.values.delete(name); + computed && value.removeDependent(computed); + }; + this.values.set(name, { value, onRemove: remove }); + return remove; + } + get(name) { + return this.values.get(name)?.value; + } + destroy() { + for (const value of this.values.values()) { + value.onRemove(); + } + } +} + +function createEffect(addValue) { + const stateCache = new WeakMap(); + const subscriptions = []; + return (subject, values) => { + const state = stateCache.get(subject) ?? new MotionValueState(); + stateCache.set(subject, state); + for (const key in values) { + const value = values[key]; + const remove = addValue(subject, state, key, value); + subscriptions.push(remove); + } + return () => { + for (const cancel of subscriptions) + cancel(); + }; + }; +} + +function canSetAsProperty(element, name) { + if (!(name in element)) + return false; + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), name) || + Object.getOwnPropertyDescriptor(element, name); + // Check if it has a setter + return descriptor && typeof descriptor.set === "function"; +} +const addAttrValue = (element, state, key, value) => { + const isProp = canSetAsProperty(element, key); + const name = isProp + ? key + : key.startsWith("data") || key.startsWith("aria") + ? camelToDash(key) + : key; + /** + * Set attribute directly via property if available + */ + const render = isProp + ? () => { + element[name] = state.latest[key]; + } + : () => { + const v = state.latest[key]; + if (v === null || v === undefined) { + element.removeAttribute(name); + } + else { + element.setAttribute(name, String(v)); + } + }; + return state.set(key, value, render); +}; +const attrEffect = /*@__PURE__*/ createSelectorEffect( +/*@__PURE__*/ createEffect(addAttrValue)); + +const propEffect = /*@__PURE__*/ createEffect((subject, state, key, value) => { + return state.set(key, value, () => { + subject[key] = state.latest[key]; + }, undefined, false); +}); + +/** + * Maximum time between the value of two frames, beyond which we + * assume the velocity has since been 0. + */ +const MAX_VELOCITY_DELTA = 30; +const isFloat = (value) => { + return !isNaN(parseFloat(value)); +}; +const collectMotionValues = { + current: undefined, +}; +/** + * `MotionValue` is used to track the state and velocity of motion values. + * + * @public + */ +class MotionValue { + /** + * @param init - The initiating value + * @param config - Optional configuration options + * + * - `transformer`: A function to transform incoming values with. + */ + constructor(init, options = {}) { + /** + * Tracks whether this value can output a velocity. Currently this is only true + * if the value is numerical, but we might be able to widen the scope here and support + * other value types. + * + * @internal + */ + this.canTrackVelocity = null; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + this.updateAndNotify = (v, render = true) => { + const currentTime = time.now(); + /** + * If we're updating the value during another frame or eventloop + * than the previous frame, then the we set the previous frame value + * to current. + */ + if (this.updatedAt !== currentTime) { + this.setPrevFrameValue(); + } + this.prev = this.current; + this.setCurrent(v); + // Update update subscribers + if (this.current !== this.prev) { + this.events.change?.notify(this.current); + if (this.dependents) { + for (const dependent of this.dependents) { + dependent.dirty(); + } + } + } + // Update render subscribers + if (render) { + this.events.renderRequest?.notify(this.current); + } + }; + this.hasAnimated = false; + this.setCurrent(init); + this.owner = options.owner; + } + setCurrent(current) { + this.current = current; + this.updatedAt = time.now(); + if (this.canTrackVelocity === null && current !== undefined) { + this.canTrackVelocity = isFloat(this.current); + } + } + setPrevFrameValue(prevFrameValue = this.current) { + this.prevFrameValue = prevFrameValue; + this.prevUpdatedAt = this.updatedAt; + } + /** + * Adds a function that will be notified when the `MotionValue` is updated. + * + * It returns a function that, when called, will cancel the subscription. + * + * When calling `onChange` inside a React component, it should be wrapped with the + * `useEffect` hook. As it returns an unsubscribe function, this should be returned + * from the `useEffect` function to ensure you don't add duplicate subscribers.. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const y = useMotionValue(0) + * const opacity = useMotionValue(1) + * + * useEffect(() => { + * function updateOpacity() { + * const maxXY = Math.max(x.get(), y.get()) + * const newOpacity = transform(maxXY, [0, 100], [1, 0]) + * opacity.set(newOpacity) + * } + * + * const unsubscribeX = x.on("change", updateOpacity) + * const unsubscribeY = y.on("change", updateOpacity) + * + * return () => { + * unsubscribeX() + * unsubscribeY() + * } + * }, []) + * + * return + * } + * ``` + * + * @param subscriber - A function that receives the latest value. + * @returns A function that, when called, will cancel this subscription. + * + * @deprecated + */ + onChange(subscription) { + if (process.env.NODE_ENV !== "production") { + motionUtils.warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`); + } + return this.on("change", subscription); + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new motionUtils.SubscriptionManager(); + } + const unsubscribe = this.events[eventName].add(callback); + if (eventName === "change") { + return () => { + unsubscribe(); + /** + * If we have no more change listeners by the start + * of the next frame, stop active animations. + */ + frame.read(() => { + if (!this.events.change.getSize()) { + this.stop(); + } + }); + }; + } + return unsubscribe; + } + clearListeners() { + for (const eventManagers in this.events) { + this.events[eventManagers].clear(); + } + } + /** + * Attaches a passive effect to the `MotionValue`. + */ + attach(passiveEffect, stopPassiveEffect) { + this.passiveEffect = passiveEffect; + this.stopPassiveEffect = stopPassiveEffect; + } + /** + * Sets the state of the `MotionValue`. + * + * @remarks + * + * ```jsx + * const x = useMotionValue(0) + * x.set(10) + * ``` + * + * @param latest - Latest value to set. + * @param render - Whether to notify render subscribers. Defaults to `true` + * + * @public + */ + set(v, render = true) { + if (!render || !this.passiveEffect) { + this.updateAndNotify(v, render); + } + else { + this.passiveEffect(v, this.updateAndNotify); + } + } + setWithVelocity(prev, current, delta) { + this.set(current); + this.prev = undefined; + this.prevFrameValue = prev; + this.prevUpdatedAt = this.updatedAt - delta; + } + /** + * Set the state of the `MotionValue`, stopping any active animations, + * effects, and resets velocity to `0`. + */ + jump(v, endAnimation = true) { + this.updateAndNotify(v); + this.prev = v; + this.prevUpdatedAt = this.prevFrameValue = undefined; + endAnimation && this.stop(); + if (this.stopPassiveEffect) + this.stopPassiveEffect(); + } + dirty() { + this.events.change?.notify(this.current); + } + addDependent(dependent) { + if (!this.dependents) { + this.dependents = new Set(); + } + this.dependents.add(dependent); + } + removeDependent(dependent) { + if (this.dependents) { + this.dependents.delete(dependent); + } + } + /** + * Returns the latest state of `MotionValue` + * + * @returns - The latest state of `MotionValue` + * + * @public + */ + get() { + if (collectMotionValues.current) { + collectMotionValues.current.push(this); + } + return this.current; + } + /** + * @public + */ + getPrevious() { + return this.prev; + } + /** + * Returns the latest velocity of `MotionValue` + * + * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. + * + * @public + */ + getVelocity() { + const currentTime = time.now(); + if (!this.canTrackVelocity || + this.prevFrameValue === undefined || + currentTime - this.updatedAt > MAX_VELOCITY_DELTA) { + return 0; + } + const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA); + // Casts because of parseFloat's poor typing + return motionUtils.velocityPerSecond(parseFloat(this.current) - + parseFloat(this.prevFrameValue), delta); + } + /** + * Registers a new animation to control this `MotionValue`. Only one + * animation can drive a `MotionValue` at one time. + * + * ```jsx + * value.start() + * ``` + * + * @param animation - A function that starts the provided animation + */ + start(startAnimation) { + this.stop(); + return new Promise((resolve) => { + this.hasAnimated = true; + this.animation = startAnimation(resolve); + if (this.events.animationStart) { + this.events.animationStart.notify(); + } + }).then(() => { + if (this.events.animationComplete) { + this.events.animationComplete.notify(); + } + this.clearAnimation(); + }); + } + /** + * Stop the currently active animation. + * + * @public + */ + stop() { + if (this.animation) { + this.animation.stop(); + if (this.events.animationCancel) { + this.events.animationCancel.notify(); + } + } + this.clearAnimation(); + } + /** + * Returns `true` if this value is currently animating. + * + * @public + */ + isAnimating() { + return !!this.animation; + } + clearAnimation() { + delete this.animation; + } + /** + * Destroy and clean up subscribers to this `MotionValue`. + * + * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically + * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually + * created a `MotionValue` via the `motionValue` function. + * + * @public + */ + destroy() { + this.dependents?.clear(); + this.events.destroy?.notify(); + this.clearListeners(); + this.stop(); + if (this.stopPassiveEffect) { + this.stopPassiveEffect(); + } + } +} +function motionValue(init, options) { + return new MotionValue(init, options); +} + +const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", +}; +function buildTransform(state) { + let transform = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < transformPropOrder.length; i++) { + const key = transformPropOrder[i]; + const value = state.latest[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + const valueToRender = state.latest[key]; + transform += `${transformName}(${valueToRender}) `; + } + } + return transformIsDefault ? "none" : transform.trim(); +} + +const originProps = new Set(["originX", "originY", "originZ"]); +const addStyleValue = (element, state, key, value) => { + let render = undefined; + let computed = undefined; + if (transformProps.has(key)) { + if (!state.get("transform")) { + // If this is an HTML element, we need to set the transform-box to fill-box + // to normalise the transform relative to the element's bounding box + if (!isHTMLElement(element) && !state.get("transformBox")) { + addStyleValue(element, state, "transformBox", new MotionValue("fill-box")); + } + state.set("transform", new MotionValue("none"), () => { + element.style.transform = buildTransform(state); + }); + } + computed = state.get("transform"); + } + else if (originProps.has(key)) { + if (!state.get("transformOrigin")) { + state.set("transformOrigin", new MotionValue(""), () => { + const originX = state.latest.originX ?? "50%"; + const originY = state.latest.originY ?? "50%"; + const originZ = state.latest.originZ ?? 0; + element.style.transformOrigin = `${originX} ${originY} ${originZ}`; + }); + } + computed = state.get("transformOrigin"); + } + else if (isCSSVar(key)) { + render = () => { + element.style.setProperty(key, state.latest[key]); + }; + } + else { + render = () => { + element.style[key] = state.latest[key]; + }; + } + return state.set(key, value, render, computed); +}; +const styleEffect = /*@__PURE__*/ createSelectorEffect( +/*@__PURE__*/ createEffect(addStyleValue)); + +const toPx = px.transform; +function addSVGPathValue(element, state, key, value) { + frame.render(() => element.setAttribute("pathLength", "1")); + if (key === "pathOffset") { + return state.set(key, value, () => element.setAttribute("stroke-dashoffset", toPx(-state.latest[key]))); + } + else { + if (!state.get("stroke-dasharray")) { + state.set("stroke-dasharray", new MotionValue("1 1"), () => { + const { pathLength = 1, pathSpacing } = state.latest; + element.setAttribute("stroke-dasharray", `${toPx(pathLength)} ${toPx(pathSpacing ?? 1 - Number(pathLength))}`); + }); + } + return state.set(key, value, undefined, state.get("stroke-dasharray")); + } +} +const addSVGValue = (element, state, key, value) => { + if (key.startsWith("path")) { + return addSVGPathValue(element, state, key, value); + } + else if (key.startsWith("attr")) { + return addAttrValue(element, state, convertAttrKey(key), value); + } + const handler = key in element.style ? addStyleValue : addAttrValue; + return handler(element, state, key, value); +}; +const svgEffect = /*@__PURE__*/ createSelectorEffect( +/*@__PURE__*/ createEffect(addSVGValue)); +function convertAttrKey(key) { + return key.replace(/^attr([A-Z])/, (_, firstChar) => firstChar.toLowerCase()); +} + +const { schedule: microtask, cancel: cancelMicrotask } = +/* @__PURE__ */ createRenderBatcher(queueMicrotask, false); + +const isDragging = { + x: false, + y: false, +}; +function isDragActive() { + return isDragging.x || isDragging.y; +} + +function setDragLock(axis) { + if (axis === "x" || axis === "y") { + if (isDragging[axis]) { + return null; + } + else { + isDragging[axis] = true; + return () => { + isDragging[axis] = false; + }; + } + } + else { + if (isDragging.x || isDragging.y) { + return null; + } + else { + isDragging.x = isDragging.y = true; + return () => { + isDragging.x = isDragging.y = false; + }; + } + } +} + +function setupGesture(elementOrSelector, options) { + const elements = resolveElements(elementOrSelector); + const gestureAbortController = new AbortController(); + const eventOptions = { + passive: true, + ...options, + signal: gestureAbortController.signal, + }; + const cancel = () => gestureAbortController.abort(); + return [elements, eventOptions, cancel]; +} + +function isValidHover(event) { + return !(event.pointerType === "touch" || isDragActive()); +} +/** + * Create a hover gesture. hover() is different to .addEventListener("pointerenter") + * in that it has an easier syntax, filters out polyfilled touch events, interoperates + * with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends. + * + * @public + */ +function hover(elementOrSelector, onHoverStart, options = {}) { + const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options); + const onPointerEnter = (enterEvent) => { + if (!isValidHover(enterEvent)) + return; + const { target } = enterEvent; + const onHoverEnd = onHoverStart(target, enterEvent); + if (typeof onHoverEnd !== "function" || !target) + return; + const onPointerLeave = (leaveEvent) => { + if (!isValidHover(leaveEvent)) + return; + onHoverEnd(leaveEvent); + target.removeEventListener("pointerleave", onPointerLeave); + }; + target.addEventListener("pointerleave", onPointerLeave, eventOptions); + }; + elements.forEach((element) => { + element.addEventListener("pointerenter", onPointerEnter, eventOptions); + }); + return cancel; +} + +/** + * Recursively traverse up the tree to check whether the provided child node + * is the parent or a descendant of it. + * + * @param parent - Element to find + * @param child - Element to test against parent + */ +const isNodeOrChild = (parent, child) => { + if (!child) { + return false; + } + else if (parent === child) { + return true; + } + else { + return isNodeOrChild(parent, child.parentElement); + } +}; + +const isPrimaryPointer = (event) => { + if (event.pointerType === "mouse") { + return typeof event.button !== "number" || event.button <= 0; + } + else { + /** + * isPrimary is true for all mice buttons, whereas every touch point + * is regarded as its own input. So subsequent concurrent touch points + * will be false. + * + * Specifically match against false here as incomplete versions of + * PointerEvents in very old browser might have it set as undefined. + */ + return event.isPrimary !== false; + } +}; + +const focusableElements = new Set([ + "BUTTON", + "INPUT", + "SELECT", + "TEXTAREA", + "A", +]); +function isElementKeyboardAccessible(element) { + return (focusableElements.has(element.tagName) || + element.tabIndex !== -1); +} + +const isPressing = new WeakSet(); + +/** + * Filter out events that are not "Enter" keys. + */ +function filterEvents(callback) { + return (event) => { + if (event.key !== "Enter") + return; + callback(event); + }; +} +function firePointerEvent(target, type) { + target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true })); +} +const enableKeyboardPress = (focusEvent, eventOptions) => { + const element = focusEvent.currentTarget; + if (!element) + return; + const handleKeydown = filterEvents(() => { + if (isPressing.has(element)) + return; + firePointerEvent(element, "down"); + const handleKeyup = filterEvents(() => { + firePointerEvent(element, "up"); + }); + const handleBlur = () => firePointerEvent(element, "cancel"); + element.addEventListener("keyup", handleKeyup, eventOptions); + element.addEventListener("blur", handleBlur, eventOptions); + }); + element.addEventListener("keydown", handleKeydown, eventOptions); + /** + * Add an event listener that fires on blur to remove the keydown events. + */ + element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions); +}; + +/** + * Filter out events that are not primary pointer events, or are triggering + * while a Motion gesture is active. + */ +function isValidPressEvent(event) { + return isPrimaryPointer(event) && !isDragActive(); +} +/** + * Create a press gesture. + * + * Press is different to `"pointerdown"`, `"pointerup"` in that it + * automatically filters out secondary pointer events like right + * click and multitouch. + * + * It also adds accessibility support for keyboards, where + * an element with a press gesture will receive focus and + * trigger on Enter `"keydown"` and `"keyup"` events. + * + * This is different to a browser's `"click"` event, which does + * respond to keyboards but only for the `"click"` itself, rather + * than the press start and end/cancel. The element also needs + * to be focusable for this to work, whereas a press gesture will + * make an element focusable by default. + * + * @public + */ +function press(targetOrSelector, onPressStart, options = {}) { + const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options); + const startPress = (startEvent) => { + const target = startEvent.currentTarget; + if (!isValidPressEvent(startEvent)) + return; + isPressing.add(target); + const onPressEnd = onPressStart(target, startEvent); + const onPointerEnd = (endEvent, success) => { + window.removeEventListener("pointerup", onPointerUp); + window.removeEventListener("pointercancel", onPointerCancel); + if (isPressing.has(target)) { + isPressing.delete(target); + } + if (!isValidPressEvent(endEvent)) { + return; + } + if (typeof onPressEnd === "function") { + onPressEnd(endEvent, { success }); + } + }; + const onPointerUp = (upEvent) => { + onPointerEnd(upEvent, target === window || + target === document || + options.useGlobalTarget || + isNodeOrChild(target, upEvent.target)); + }; + const onPointerCancel = (cancelEvent) => { + onPointerEnd(cancelEvent, false); + }; + window.addEventListener("pointerup", onPointerUp, eventOptions); + window.addEventListener("pointercancel", onPointerCancel, eventOptions); + }; + targets.forEach((target) => { + const pointerDownTarget = options.useGlobalTarget ? window : target; + pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions); + if (isHTMLElement(target)) { + target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions)); + if (!isElementKeyboardAccessible(target) && + !target.hasAttribute("tabindex")) { + target.tabIndex = 0; + } + } + }); + return cancelEvents; +} + +function getComputedStyle$1(element, name) { + const computedStyle = window.getComputedStyle(element); + return isCSSVar(name) + ? computedStyle.getPropertyValue(name) + : computedStyle[name]; +} + +/** + * Checks if an element is an SVG element in a way + * that works across iframes + */ +function isSVGElement(element) { + return motionUtils.isObject(element) && "ownerSVGElement" in element; +} + +const resizeHandlers = new WeakMap(); +let observer; +const getSize = (borderBoxAxis, svgAxis, htmlAxis) => (target, borderBoxSize) => { + if (borderBoxSize && borderBoxSize[0]) { + return borderBoxSize[0][(borderBoxAxis + "Size")]; + } + else if (isSVGElement(target) && "getBBox" in target) { + return target.getBBox()[svgAxis]; + } + else { + return target[htmlAxis]; + } +}; +const getWidth = /*@__PURE__*/ getSize("inline", "width", "offsetWidth"); +const getHeight = /*@__PURE__*/ getSize("block", "height", "offsetHeight"); +function notifyTarget({ target, borderBoxSize }) { + resizeHandlers.get(target)?.forEach((handler) => { + handler(target, { + get width() { + return getWidth(target, borderBoxSize); + }, + get height() { + return getHeight(target, borderBoxSize); + }, + }); + }); +} +function notifyAll(entries) { + entries.forEach(notifyTarget); +} +function createResizeObserver() { + if (typeof ResizeObserver === "undefined") + return; + observer = new ResizeObserver(notifyAll); +} +function resizeElement(target, handler) { + if (!observer) + createResizeObserver(); + const elements = resolveElements(target); + elements.forEach((element) => { + let elementHandlers = resizeHandlers.get(element); + if (!elementHandlers) { + elementHandlers = new Set(); + resizeHandlers.set(element, elementHandlers); + } + elementHandlers.add(handler); + observer?.observe(element); + }); + return () => { + elements.forEach((element) => { + const elementHandlers = resizeHandlers.get(element); + elementHandlers?.delete(handler); + if (!elementHandlers?.size) { + observer?.unobserve(element); + } + }); + }; +} + +const windowCallbacks = new Set(); +let windowResizeHandler; +function createWindowResizeHandler() { + windowResizeHandler = () => { + const info = { + get width() { + return window.innerWidth; + }, + get height() { + return window.innerHeight; + }, + }; + windowCallbacks.forEach((callback) => callback(info)); + }; + window.addEventListener("resize", windowResizeHandler); +} +function resizeWindow(callback) { + windowCallbacks.add(callback); + if (!windowResizeHandler) + createWindowResizeHandler(); + return () => { + windowCallbacks.delete(callback); + if (!windowCallbacks.size && + typeof windowResizeHandler === "function") { + window.removeEventListener("resize", windowResizeHandler); + windowResizeHandler = undefined; + } + }; +} + +function resize(a, b) { + return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b); +} + +function observeTimeline(update, timeline) { + let prevProgress; + const onFrame = () => { + const { currentTime } = timeline; + const percentage = currentTime === null ? 0 : currentTime.value; + const progress = percentage / 100; + if (prevProgress !== progress) { + update(progress); + } + prevProgress = progress; + }; + frame.preUpdate(onFrame, true); + return () => cancelFrame(onFrame); +} + +function record() { + const { value } = statsBuffer; + if (value === null) { + cancelFrame(record); + return; + } + value.frameloop.rate.push(frameData.delta); + value.animations.mainThread.push(activeAnimations.mainThread); + value.animations.waapi.push(activeAnimations.waapi); + value.animations.layout.push(activeAnimations.layout); +} +function mean(values) { + return values.reduce((acc, value) => acc + value, 0) / values.length; +} +function summarise(values, calcAverage = mean) { + if (values.length === 0) { + return { + min: 0, + max: 0, + avg: 0, + }; + } + return { + min: Math.min(...values), + max: Math.max(...values), + avg: calcAverage(values), + }; +} +const msToFps = (ms) => Math.round(1000 / ms); +function clearStatsBuffer() { + statsBuffer.value = null; + statsBuffer.addProjectionMetrics = null; +} +function reportStats() { + const { value } = statsBuffer; + if (!value) { + throw new Error("Stats are not being measured"); + } + clearStatsBuffer(); + cancelFrame(record); + const summary = { + frameloop: { + setup: summarise(value.frameloop.setup), + rate: summarise(value.frameloop.rate), + read: summarise(value.frameloop.read), + resolveKeyframes: summarise(value.frameloop.resolveKeyframes), + preUpdate: summarise(value.frameloop.preUpdate), + update: summarise(value.frameloop.update), + preRender: summarise(value.frameloop.preRender), + render: summarise(value.frameloop.render), + postRender: summarise(value.frameloop.postRender), + }, + animations: { + mainThread: summarise(value.animations.mainThread), + waapi: summarise(value.animations.waapi), + layout: summarise(value.animations.layout), + }, + layoutProjection: { + nodes: summarise(value.layoutProjection.nodes), + calculatedTargetDeltas: summarise(value.layoutProjection.calculatedTargetDeltas), + calculatedProjections: summarise(value.layoutProjection.calculatedProjections), + }, + }; + /** + * Convert the rate to FPS + */ + const { rate } = summary.frameloop; + rate.min = msToFps(rate.min); + rate.max = msToFps(rate.max); + rate.avg = msToFps(rate.avg); + [rate.min, rate.max] = [rate.max, rate.min]; + return summary; +} +function recordStats() { + if (statsBuffer.value) { + clearStatsBuffer(); + throw new Error("Stats are already being measured"); + } + const newStatsBuffer = statsBuffer; + newStatsBuffer.value = { + frameloop: { + setup: [], + rate: [], + read: [], + resolveKeyframes: [], + preUpdate: [], + update: [], + preRender: [], + render: [], + postRender: [], + }, + animations: { + mainThread: [], + waapi: [], + layout: [], + }, + layoutProjection: { + nodes: [], + calculatedTargetDeltas: [], + calculatedProjections: [], + }, + }; + newStatsBuffer.addProjectionMetrics = (metrics) => { + const { layoutProjection } = newStatsBuffer.value; + layoutProjection.nodes.push(metrics.nodes); + layoutProjection.calculatedTargetDeltas.push(metrics.calculatedTargetDeltas); + layoutProjection.calculatedProjections.push(metrics.calculatedProjections); + }; + frame.postRender(record, true); + return reportStats; +} + +/** + * Checks if an element is specifically an SVGSVGElement (the root SVG element) + * in a way that works across iframes + */ +function isSVGSVGElement(element) { + return isSVGElement(element) && element.tagName === "svg"; +} + +function getOriginIndex(from, total) { + if (from === "first") { + return 0; + } + else { + const lastIndex = total - 1; + return from === "last" ? lastIndex : lastIndex / 2; + } +} +function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) { + return (i, total) => { + const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total); + const distance = Math.abs(fromIndex - i); + let delay = duration * distance; + if (ease) { + const maxDelay = total * duration; + const easingFunction = motionUtils.easingDefinitionToFunction(ease); + delay = easingFunction(delay / maxDelay) * maxDelay; + } + return startDelay + delay; + }; +} + +function transform(...args) { + const useImmediate = !Array.isArray(args[0]); + const argOffset = useImmediate ? 0 : -1; + const inputValue = args[0 + argOffset]; + const inputRange = args[1 + argOffset]; + const outputRange = args[2 + argOffset]; + const options = args[3 + argOffset]; + const interpolator = interpolate(inputRange, outputRange, options); + return useImmediate ? interpolator(inputValue) : interpolator; +} + +function subscribeValue(inputValues, outputValue, getLatest) { + const update = () => outputValue.set(getLatest()); + const scheduleUpdate = () => frame.preRender(update, false, true); + const subscriptions = inputValues.map((v) => v.on("change", scheduleUpdate)); + outputValue.on("destroy", () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + cancelFrame(update); + }); +} + +/** + * Create a `MotionValue` that transforms the output of other `MotionValue`s by + * passing their latest values through a transform function. + * + * Whenever a `MotionValue` referred to in the provided function is updated, + * it will be re-evaluated. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ +function transformValue(transform) { + const collectedValues = []; + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * inside transform will be saved into this array. + */ + collectMotionValues.current = collectedValues; + const initialValue = transform(); + collectMotionValues.current = undefined; + const value = motionValue(initialValue); + subscribeValue(collectedValues, value, transform); + return value; +} + +/** + * Create a `MotionValue` that maps the output of another `MotionValue` by + * mapping it from one range of values into another. + * + * @remarks + * + * Given an input range of `[-200, -100, 100, 200]` and an output range of + * `[0, 1, 1, 0]`, the returned `MotionValue` will: + * + * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`. + * - When provided a value between `-100` and `100`, will return `1`. + * - When provided a value between `100` and `200`, will return a value between `1` and `0` + * + * The input range must be a linear series of numbers. The output range + * can be any value type supported by Motion: numbers, colors, shadows, etc. + * + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * const x = motionValue(0) + * const xRange = [-200, -100, 100, 200] + * const opacityRange = [0, 1, 1, 0] + * const opacity = mapValue(x, xRange, opacityRange) + * ``` + * + * @param inputValue - `MotionValue` + * @param inputRange - A linear series of numbers (either all increasing or decreasing) + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - + * + * - clamp: boolean. Clamp values to within the given range. Defaults to `true` + * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each. + * + * @returns `MotionValue` + * + * @public + */ +function mapValue(inputValue, inputRange, outputRange, options) { + const map = transform(inputRange, outputRange, options); + return transformValue(() => map(inputValue.get())); +} + +const isMotionValue = (value) => Boolean(value && value.getVelocity); + +/** + * Create a `MotionValue` that animates to its latest value using a spring. + * Can either be a value or track another `MotionValue`. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ +function springValue(source, options) { + const initialValue = isMotionValue(source) ? source.get() : source; + const value = motionValue(initialValue); + attachSpring(value, source, options); + return value; +} +function attachSpring(value, source, options) { + const initialValue = value.get(); + let activeAnimation = null; + let latestValue = initialValue; + let latestSetter; + const unit = typeof initialValue === "string" + ? initialValue.replace(/[\d.-]/g, "") + : undefined; + const stopAnimation = () => { + if (activeAnimation) { + activeAnimation.stop(); + activeAnimation = null; + } + }; + const startAnimation = () => { + stopAnimation(); + activeAnimation = new JSAnimation({ + keyframes: [asNumber(value.get()), asNumber(latestValue)], + velocity: value.getVelocity(), + type: "spring", + restDelta: 0.001, + restSpeed: 0.01, + ...options, + onUpdate: latestSetter, + }); + }; + value.attach((v, set) => { + latestValue = v; + latestSetter = (latest) => set(parseValue(latest, unit)); + frame.postRender(startAnimation); + return value.get(); + }, stopAnimation); + let unsubscribe = undefined; + if (isMotionValue(source)) { + unsubscribe = source.on("change", (v) => value.set(parseValue(v, unit))); + value.on("destroy", unsubscribe); + } + return unsubscribe; +} +function parseValue(v, unit) { + return unit ? v + unit : v; +} +function asNumber(v) { + return typeof v === "number" ? v : parseFloat(v); +} + +/** + * A list of all ValueTypes + */ +const valueTypes = [...dimensionValueTypes, color, complex]; +/** + * Tests a value against the list of ValueTypes + */ +const findValueType = (v) => valueTypes.find(testValueType(v)); + +function chooseLayerType(valueName) { + if (valueName === "layout") + return "group"; + if (valueName === "enter" || valueName === "new") + return "new"; + if (valueName === "exit" || valueName === "old") + return "old"; + return "group"; +} + +let pendingRules = {}; +let style = null; +const css = { + set: (selector, values) => { + pendingRules[selector] = values; + }, + commit: () => { + if (!style) { + style = document.createElement("style"); + style.id = "motion-view"; + } + let cssText = ""; + for (const selector in pendingRules) { + const rule = pendingRules[selector]; + cssText += `${selector} {\n`; + for (const [property, value] of Object.entries(rule)) { + cssText += ` ${property}: ${value};\n`; + } + cssText += "}\n"; + } + style.textContent = cssText; + document.head.appendChild(style); + pendingRules = {}; + }, + remove: () => { + if (style && style.parentElement) { + style.parentElement.removeChild(style); + } + }, +}; + +function getLayerName(pseudoElement) { + const match = pseudoElement.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/); + if (!match) + return null; + return { layer: match[2], type: match[1] }; +} + +function filterViewAnimations(animation) { + const { effect } = animation; + if (!effect) + return false; + return (effect.target === document.documentElement && + effect.pseudoElement?.startsWith("::view-transition")); +} +function getViewAnimations() { + return document.getAnimations().filter(filterViewAnimations); +} + +function hasTarget(target, targets) { + return targets.has(target) && Object.keys(targets.get(target)).length > 0; +} + +const definitionNames = ["layout", "enter", "exit", "new", "old"]; +function startViewAnimation(builder) { + const { update, targets, options: defaultOptions } = builder; + if (!document.startViewTransition) { + return new Promise(async (resolve) => { + await update(); + resolve(new GroupAnimation([])); + }); + } + // TODO: Go over existing targets and ensure they all have ids + /** + * If we don't have any animations defined for the root target, + * remove it from being captured. + */ + if (!hasTarget("root", targets)) { + css.set(":root", { + "view-transition-name": "none", + }); + } + /** + * Set the timing curve to linear for all view transition layers. + * This gets baked into the keyframes, which can't be changed + * without breaking the generated animation. + * + * This allows us to set easing via updateTiming - which can be changed. + */ + css.set("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)", { "animation-timing-function": "linear !important" }); + css.commit(); // Write + const transition = document.startViewTransition(async () => { + await update(); + // TODO: Go over new targets and ensure they all have ids + }); + transition.finished.finally(() => { + css.remove(); // Write + }); + return new Promise((resolve) => { + transition.ready.then(() => { + const generatedViewAnimations = getViewAnimations(); + const animations = []; + /** + * Create animations for each of our explicitly-defined subjects. + */ + targets.forEach((definition, target) => { + // TODO: If target is not "root", resolve elements + // and iterate over each + for (const key of definitionNames) { + if (!definition[key]) + continue; + const { keyframes, options } = definition[key]; + for (let [valueName, valueKeyframes] of Object.entries(keyframes)) { + if (!valueKeyframes) + continue; + const valueOptions = { + ...getValueTransition(defaultOptions, valueName), + ...getValueTransition(options, valueName), + }; + const type = chooseLayerType(key); + /** + * If this is an opacity animation, and keyframes are not an array, + * we need to convert them into an array and set an initial value. + */ + if (valueName === "opacity" && + !Array.isArray(valueKeyframes)) { + const initialValue = type === "new" ? 0 : 1; + valueKeyframes = [initialValue, valueKeyframes]; + } + /** + * Resolve stagger function if provided. + */ + if (typeof valueOptions.delay === "function") { + valueOptions.delay = valueOptions.delay(0, 1); + } + valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay)); + const animation = new NativeAnimation({ + ...valueOptions, + element: document.documentElement, + name: valueName, + pseudoElement: `::view-transition-${type}(${target})`, + keyframes: valueKeyframes, + }); + animations.push(animation); + } + } + }); + /** + * Handle browser generated animations + */ + for (const animation of generatedViewAnimations) { + if (animation.playState === "finished") + continue; + const { effect } = animation; + if (!effect || !(effect instanceof KeyframeEffect)) + continue; + const { pseudoElement } = effect; + if (!pseudoElement) + continue; + const name = getLayerName(pseudoElement); + if (!name) + continue; + const targetDefinition = targets.get(name.layer); + if (!targetDefinition) { + /** + * If transition name is group then update the timing of the animation + * whereas if it's old or new then we could possibly replace it using + * the above method. + */ + const transitionName = name.type === "group" ? "layout" : ""; + let animationTransition = { + ...getValueTransition(defaultOptions, transitionName), + }; + animationTransition.duration && (animationTransition.duration = motionUtils.secondsToMilliseconds(animationTransition.duration)); + animationTransition = + applyGeneratorOptions(animationTransition); + const easing = mapEasingToNativeEasing(animationTransition.ease, animationTransition.duration); + effect.updateTiming({ + delay: motionUtils.secondsToMilliseconds(animationTransition.delay ?? 0), + duration: animationTransition.duration, + easing, + }); + animations.push(new NativeAnimationWrapper(animation)); + } + else if (hasOpacity(targetDefinition, "enter") && + hasOpacity(targetDefinition, "exit") && + effect + .getKeyframes() + .some((keyframe) => keyframe.mixBlendMode)) { + animations.push(new NativeAnimationWrapper(animation)); + } + else { + animation.cancel(); + } + } + resolve(new GroupAnimation(animations)); + }); + }); +} +function hasOpacity(target, key) { + return target?.[key]?.keyframes.opacity; +} + +let builders = []; +let current = null; +function next() { + current = null; + const [nextBuilder] = builders; + if (nextBuilder) + start(nextBuilder); +} +function start(builder) { + motionUtils.removeItem(builders, builder); + current = builder; + startViewAnimation(builder).then((animation) => { + builder.notifyReady(animation); + animation.finished.finally(next); + }); +} +function processQueue() { + /** + * Iterate backwards over the builders array. We can ignore the + * "wait" animations. If we have an interrupting animation in the + * queue then we need to batch all preceeding animations into it. + * Currently this only batches the update functions but will also + * need to batch the targets. + */ + for (let i = builders.length - 1; i >= 0; i--) { + const builder = builders[i]; + const { interrupt } = builder.options; + if (interrupt === "immediate") { + const batchedUpdates = builders.slice(0, i + 1).map((b) => b.update); + const remaining = builders.slice(i + 1); + builder.update = () => { + batchedUpdates.forEach((update) => update()); + }; + // Put the current builder at the front, followed by any "wait" builders + builders = [builder, ...remaining]; + break; + } + } + if (!current || builders[0]?.options.interrupt === "immediate") { + next(); + } +} +function addToQueue(builder) { + builders.push(builder); + microtask.render(processQueue); +} + +class ViewTransitionBuilder { + constructor(update, options = {}) { + this.currentSubject = "root"; + this.targets = new Map(); + this.notifyReady = motionUtils.noop; + this.readyPromise = new Promise((resolve) => { + this.notifyReady = resolve; + }); + this.update = update; + this.options = { + interrupt: "wait", + ...options, + }; + addToQueue(this); + } + get(subject) { + this.currentSubject = subject; + return this; + } + layout(keyframes, options) { + this.updateTarget("layout", keyframes, options); + return this; + } + new(keyframes, options) { + this.updateTarget("new", keyframes, options); + return this; + } + old(keyframes, options) { + this.updateTarget("old", keyframes, options); + return this; + } + enter(keyframes, options) { + this.updateTarget("enter", keyframes, options); + return this; + } + exit(keyframes, options) { + this.updateTarget("exit", keyframes, options); + return this; + } + crossfade(options) { + this.updateTarget("enter", { opacity: 1 }, options); + this.updateTarget("exit", { opacity: 0 }, options); + return this; + } + updateTarget(target, keyframes, options = {}) { + const { currentSubject, targets } = this; + if (!targets.has(currentSubject)) { + targets.set(currentSubject, {}); + } + const targetData = targets.get(currentSubject); + targetData[target] = { keyframes, options }; + } + then(resolve, reject) { + return this.readyPromise.then(resolve, reject); + } +} +function animateView(update, defaultOptions = {}) { + return new ViewTransitionBuilder(update, defaultOptions); +} + +/** + * @deprecated + * + * Import as `frame` instead. + */ +const sync = frame; +/** + * @deprecated + * + * Use cancelFrame(callback) instead. + */ +const cancelSync = stepsOrder.reduce((acc, key) => { + acc[key] = (process) => cancelFrame(process); + return acc; +}, {}); + +exports.AsyncMotionValueAnimation = AsyncMotionValueAnimation; +exports.DOMKeyframesResolver = DOMKeyframesResolver; +exports.GroupAnimation = GroupAnimation; +exports.GroupAnimationWithThen = GroupAnimationWithThen; +exports.JSAnimation = JSAnimation; +exports.KeyframeResolver = KeyframeResolver; +exports.MotionValue = MotionValue; +exports.NativeAnimation = NativeAnimation; +exports.NativeAnimationExtended = NativeAnimationExtended; +exports.NativeAnimationWrapper = NativeAnimationWrapper; +exports.ViewTransitionBuilder = ViewTransitionBuilder; +exports.acceleratedValues = acceleratedValues; +exports.activeAnimations = activeAnimations; +exports.addAttrValue = addAttrValue; +exports.addStyleValue = addStyleValue; +exports.alpha = alpha; +exports.analyseComplexValue = analyseComplexValue; +exports.animateValue = animateValue; +exports.animateView = animateView; +exports.animationMapKey = animationMapKey; +exports.applyPxDefaults = applyPxDefaults; +exports.attachSpring = attachSpring; +exports.attrEffect = attrEffect; +exports.calcGeneratorDuration = calcGeneratorDuration; +exports.cancelFrame = cancelFrame; +exports.cancelMicrotask = cancelMicrotask; +exports.cancelSync = cancelSync; +exports.collectMotionValues = collectMotionValues; +exports.color = color; +exports.complex = complex; +exports.convertOffsetToTimes = convertOffsetToTimes; +exports.createGeneratorEasing = createGeneratorEasing; +exports.createRenderBatcher = createRenderBatcher; +exports.cubicBezierAsString = cubicBezierAsString; +exports.defaultEasing = defaultEasing; +exports.defaultOffset = defaultOffset; +exports.defaultTransformValue = defaultTransformValue; +exports.defaultValueTypes = defaultValueTypes; +exports.degrees = degrees; +exports.dimensionValueTypes = dimensionValueTypes; +exports.fillOffset = fillOffset; +exports.fillWildcards = fillWildcards; +exports.findDimensionValueType = findDimensionValueType; +exports.findValueType = findValueType; +exports.flushKeyframeResolvers = flushKeyframeResolvers; +exports.frame = frame; +exports.frameData = frameData; +exports.frameSteps = frameSteps; +exports.generateLinearEasing = generateLinearEasing; +exports.getAnimatableNone = getAnimatableNone; +exports.getAnimationMap = getAnimationMap; +exports.getComputedStyle = getComputedStyle$1; +exports.getDefaultValueType = getDefaultValueType; +exports.getMixer = getMixer; +exports.getOriginIndex = getOriginIndex; +exports.getValueAsType = getValueAsType; +exports.getValueTransition = getValueTransition; +exports.getVariableValue = getVariableValue; +exports.hex = hex; +exports.hover = hover; +exports.hsla = hsla; +exports.hslaToRgba = hslaToRgba; +exports.inertia = inertia; +exports.interpolate = interpolate; +exports.invisibleValues = invisibleValues; +exports.isCSSVariableName = isCSSVariableName; +exports.isCSSVariableToken = isCSSVariableToken; +exports.isDragActive = isDragActive; +exports.isDragging = isDragging; +exports.isGenerator = isGenerator; +exports.isHTMLElement = isHTMLElement; +exports.isMotionValue = isMotionValue; +exports.isNodeOrChild = isNodeOrChild; +exports.isPrimaryPointer = isPrimaryPointer; +exports.isSVGElement = isSVGElement; +exports.isSVGSVGElement = isSVGSVGElement; +exports.isWaapiSupportedEasing = isWaapiSupportedEasing; +exports.keyframes = keyframes; +exports.mapEasingToNativeEasing = mapEasingToNativeEasing; +exports.mapValue = mapValue; +exports.maxGeneratorDuration = maxGeneratorDuration; +exports.microtask = microtask; +exports.mix = mix; +exports.mixArray = mixArray; +exports.mixColor = mixColor; +exports.mixComplex = mixComplex; +exports.mixImmediate = mixImmediate; +exports.mixLinearColor = mixLinearColor; +exports.mixNumber = mixNumber$1; +exports.mixObject = mixObject; +exports.mixVisibility = mixVisibility; +exports.motionValue = motionValue; +exports.number = number; +exports.numberValueTypes = numberValueTypes; +exports.observeTimeline = observeTimeline; +exports.parseCSSVariable = parseCSSVariable; +exports.parseValueFromTransform = parseValueFromTransform; +exports.percent = percent; +exports.positionalKeys = positionalKeys; +exports.press = press; +exports.progressPercentage = progressPercentage; +exports.propEffect = propEffect; +exports.px = px; +exports.readTransformValue = readTransformValue; +exports.recordStats = recordStats; +exports.resize = resize; +exports.resolveElements = resolveElements; +exports.rgbUnit = rgbUnit; +exports.rgba = rgba; +exports.scale = scale; +exports.setDragLock = setDragLock; +exports.setStyle = setStyle; +exports.spring = spring; +exports.springValue = springValue; +exports.stagger = stagger; +exports.startWaapiAnimation = startWaapiAnimation; +exports.statsBuffer = statsBuffer; +exports.styleEffect = styleEffect; +exports.supportedWaapiEasing = supportedWaapiEasing; +exports.supportsBrowserAnimation = supportsBrowserAnimation; +exports.supportsFlags = supportsFlags; +exports.supportsLinearEasing = supportsLinearEasing; +exports.supportsPartialKeyframes = supportsPartialKeyframes; +exports.supportsScrollTimeline = supportsScrollTimeline; +exports.svgEffect = svgEffect; +exports.sync = sync; +exports.testValueType = testValueType; +exports.time = time; +exports.transform = transform; +exports.transformPropOrder = transformPropOrder; +exports.transformProps = transformProps; +exports.transformValue = transformValue; +exports.transformValueTypes = transformValueTypes; +exports.vh = vh; +exports.vw = vw; diff --git a/node_modules/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs b/node_modules/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs new file mode 100644 index 00000000..95f2ff61 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs @@ -0,0 +1,174 @@ +import { MotionGlobalConfig, noop } from 'motion-utils'; +import { time } from '../frameloop/sync-time.mjs'; +import { JSAnimation } from './JSAnimation.mjs'; +import { getFinalKeyframe } from './keyframes/get-final.mjs'; +import { KeyframeResolver, flushKeyframeResolvers } from './keyframes/KeyframesResolver.mjs'; +import { NativeAnimationExtended } from './NativeAnimationExtended.mjs'; +import { canAnimate } from './utils/can-animate.mjs'; +import { WithPromise } from './utils/WithPromise.mjs'; +import { supportsBrowserAnimation } from './waapi/supports/waapi.mjs'; + +/** + * Maximum time allowed between an animation being created and it being + * resolved for us to use the latter as the start time. + * + * This is to ensure that while we prefer to "start" an animation as soon + * as it's triggered, we also want to avoid a visual jump if there's a big delay + * between these two moments. + */ +const MAX_RESOLVE_DELAY = 40; +class AsyncMotionValueAnimation extends WithPromise { + constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) { + super(); + /** + * Bound to support return animation.stop pattern + */ + this.stop = () => { + if (this._animation) { + this._animation.stop(); + this.stopTimeline?.(); + } + this.keyframeResolver?.cancel(); + }; + this.createdAt = time.now(); + const optionsWithDefaults = { + autoplay, + delay, + type, + repeat, + repeatDelay, + repeatType, + name, + motionValue, + element, + ...options, + }; + const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver; + this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element); + this.keyframeResolver?.scheduleResolve(); + } + onKeyframesResolved(keyframes, finalKeyframe, options, sync) { + this.keyframeResolver = undefined; + const { name, type, velocity, delay, isHandoff, onUpdate } = options; + this.resolvedAt = time.now(); + /** + * If we can't animate this value with the resolved keyframes + * then we should complete it immediately. + */ + if (!canAnimate(keyframes, name, type, velocity)) { + if (MotionGlobalConfig.instantAnimations || !delay) { + onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe)); + } + keyframes[0] = keyframes[keyframes.length - 1]; + options.duration = 0; + options.repeat = 0; + } + /** + * Resolve startTime for the animation. + * + * This method uses the createdAt and resolvedAt to calculate the + * animation startTime. *Ideally*, we would use the createdAt time as t=0 + * as the following frame would then be the first frame of the animation in + * progress, which would feel snappier. + * + * However, if there's a delay (main thread work) between the creation of + * the animation and the first commited frame, we prefer to use resolvedAt + * to avoid a sudden jump into the animation. + */ + const startTime = sync + ? !this.resolvedAt + ? this.createdAt + : this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY + ? this.resolvedAt + : this.createdAt + : undefined; + const resolvedOptions = { + startTime, + finalKeyframe, + ...options, + keyframes, + }; + /** + * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via + * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the + * optimised animation. + */ + const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions) + ? new NativeAnimationExtended({ + ...resolvedOptions, + element: resolvedOptions.motionValue.owner.current, + }) + : new JSAnimation(resolvedOptions); + animation.finished.then(() => this.notifyFinished()).catch(noop); + if (this.pendingTimeline) { + this.stopTimeline = animation.attachTimeline(this.pendingTimeline); + this.pendingTimeline = undefined; + } + this._animation = animation; + } + get finished() { + if (!this._animation) { + return this._finished; + } + else { + return this.animation.finished; + } + } + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } + get animation() { + if (!this._animation) { + this.keyframeResolver?.resume(); + flushKeyframeResolvers(); + } + return this._animation; + } + get duration() { + return this.animation.duration; + } + get time() { + return this.animation.time; + } + set time(newTime) { + this.animation.time = newTime; + } + get speed() { + return this.animation.speed; + } + get state() { + return this.animation.state; + } + set speed(newSpeed) { + this.animation.speed = newSpeed; + } + get startTime() { + return this.animation.startTime; + } + attachTimeline(timeline) { + if (this._animation) { + this.stopTimeline = this.animation.attachTimeline(timeline); + } + else { + this.pendingTimeline = timeline; + } + return () => this.stop(); + } + play() { + this.animation.play(); + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.complete(); + } + cancel() { + if (this._animation) { + this.animation.cancel(); + } + this.keyframeResolver?.cancel(); + } +} + +export { AsyncMotionValueAnimation }; diff --git a/node_modules/motion-dom/dist/es/animation/GroupAnimation.mjs b/node_modules/motion-dom/dist/es/animation/GroupAnimation.mjs new file mode 100644 index 00000000..3dcb9ad4 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/GroupAnimation.mjs @@ -0,0 +1,72 @@ +class GroupAnimation { + constructor(animations) { + // Bound to accomadate common `return animation.stop` pattern + this.stop = () => this.runAll("stop"); + this.animations = animations.filter(Boolean); + } + get finished() { + return Promise.all(this.animations.map((animation) => animation.finished)); + } + /** + * TODO: Filter out cancelled or stopped animations before returning + */ + getAll(propName) { + return this.animations[0][propName]; + } + setAll(propName, newValue) { + for (let i = 0; i < this.animations.length; i++) { + this.animations[i][propName] = newValue; + } + } + attachTimeline(timeline) { + const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline)); + return () => { + subscriptions.forEach((cancel, i) => { + cancel && cancel(); + this.animations[i].stop(); + }); + }; + } + get time() { + return this.getAll("time"); + } + set time(time) { + this.setAll("time", time); + } + get speed() { + return this.getAll("speed"); + } + set speed(speed) { + this.setAll("speed", speed); + } + get state() { + return this.getAll("state"); + } + get startTime() { + return this.getAll("startTime"); + } + get duration() { + let max = 0; + for (let i = 0; i < this.animations.length; i++) { + max = Math.max(max, this.animations[i].duration); + } + return max; + } + runAll(methodName) { + this.animations.forEach((controls) => controls[methodName]()); + } + play() { + this.runAll("play"); + } + pause() { + this.runAll("pause"); + } + cancel() { + this.runAll("cancel"); + } + complete() { + this.runAll("complete"); + } +} + +export { GroupAnimation }; diff --git a/node_modules/motion-dom/dist/es/animation/GroupAnimationWithThen.mjs b/node_modules/motion-dom/dist/es/animation/GroupAnimationWithThen.mjs new file mode 100644 index 00000000..aca10f33 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/GroupAnimationWithThen.mjs @@ -0,0 +1,9 @@ +import { GroupAnimation } from './GroupAnimation.mjs'; + +class GroupAnimationWithThen extends GroupAnimation { + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } +} + +export { GroupAnimationWithThen }; diff --git a/node_modules/motion-dom/dist/es/animation/JSAnimation.mjs b/node_modules/motion-dom/dist/es/animation/JSAnimation.mjs new file mode 100644 index 00000000..5140ac4a --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/JSAnimation.mjs @@ -0,0 +1,345 @@ +import { invariant, pipe, clamp, millisecondsToSeconds, secondsToMilliseconds } from 'motion-utils'; +import { time } from '../frameloop/sync-time.mjs'; +import { activeAnimations } from '../stats/animation-count.mjs'; +import { mix } from '../utils/mix/index.mjs'; +import { frameloopDriver } from './drivers/frame.mjs'; +import { inertia } from './generators/inertia.mjs'; +import { keyframes } from './generators/keyframes.mjs'; +import { calcGeneratorDuration } from './generators/utils/calc-duration.mjs'; +import { getFinalKeyframe } from './keyframes/get-final.mjs'; +import { replaceTransitionType } from './utils/replace-transition-type.mjs'; +import { WithPromise } from './utils/WithPromise.mjs'; + +const percentToProgress = (percent) => percent / 100; +class JSAnimation extends WithPromise { + constructor(options) { + super(); + this.state = "idle"; + this.startTime = null; + this.isStopped = false; + /** + * The current time of the animation. + */ + this.currentTime = 0; + /** + * The time at which the animation was paused. + */ + this.holdTime = null; + /** + * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed. + */ + this.playbackSpeed = 1; + /** + * This method is bound to the instance to fix a pattern where + * animation.stop is returned as a reference from a useEffect. + */ + this.stop = () => { + const { motionValue } = this.options; + if (motionValue && motionValue.updatedAt !== time.now()) { + this.tick(time.now()); + } + this.isStopped = true; + if (this.state === "idle") + return; + this.teardown(); + this.options.onStop?.(); + }; + activeAnimations.mainThread++; + this.options = options; + this.initAnimation(); + this.play(); + if (options.autoplay === false) + this.pause(); + } + initAnimation() { + const { options } = this; + replaceTransitionType(options); + const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options; + let { keyframes: keyframes$1 } = options; + const generatorFactory = type || keyframes; + if (process.env.NODE_ENV !== "production" && + generatorFactory !== keyframes) { + invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`); + } + if (generatorFactory !== keyframes && + typeof keyframes$1[0] !== "number") { + this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1])); + keyframes$1 = [0, 100]; + } + const generator = generatorFactory({ ...options, keyframes: keyframes$1 }); + /** + * If we have a mirror repeat type we need to create a second generator that outputs the + * mirrored (not reversed) animation and later ping pong between the two generators. + */ + if (repeatType === "mirror") { + this.mirroredGenerator = generatorFactory({ + ...options, + keyframes: [...keyframes$1].reverse(), + velocity: -velocity, + }); + } + /** + * If duration is undefined and we have repeat options, + * we need to calculate a duration from the generator. + * + * We set it to the generator itself to cache the duration. + * Any timeline resolver will need to have already precalculated + * the duration by this step. + */ + if (generator.calculatedDuration === null) { + generator.calculatedDuration = calcGeneratorDuration(generator); + } + const { calculatedDuration } = generator; + this.calculatedDuration = calculatedDuration; + this.resolvedDuration = calculatedDuration + repeatDelay; + this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay; + this.generator = generator; + } + updateTime(timestamp) { + const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed; + // Update currentTime + if (this.holdTime !== null) { + this.currentTime = this.holdTime; + } + else { + // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 = + // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for + // example. + this.currentTime = animationTime; + } + } + tick(timestamp, sample = false) { + const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this; + if (this.startTime === null) + return generator.next(0); + const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options; + /** + * requestAnimationFrame timestamps can come through as lower than + * the startTime as set by performance.now(). Here we prevent this, + * though in the future it could be possible to make setting startTime + * a pending operation that gets resolved here. + */ + if (this.speed > 0) { + this.startTime = Math.min(this.startTime, timestamp); + } + else if (this.speed < 0) { + this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime); + } + if (sample) { + this.currentTime = timestamp; + } + else { + this.updateTime(timestamp); + } + // Rebase on delay + const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1); + const isInDelayPhase = this.playbackSpeed >= 0 + ? timeWithoutDelay < 0 + : timeWithoutDelay > totalDuration; + this.currentTime = Math.max(timeWithoutDelay, 0); + // If this animation has finished, set the current time to the total duration. + if (this.state === "finished" && this.holdTime === null) { + this.currentTime = totalDuration; + } + let elapsed = this.currentTime; + let frameGenerator = generator; + if (repeat) { + /** + * Get the current progress (0-1) of the animation. If t is > + * than duration we'll get values like 2.5 (midway through the + * third iteration) + */ + const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration; + /** + * Get the current iteration (0 indexed). For instance the floor of + * 2.5 is 2. + */ + let currentIteration = Math.floor(progress); + /** + * Get the current progress of the iteration by taking the remainder + * so 2.5 is 0.5 through iteration 2 + */ + let iterationProgress = progress % 1.0; + /** + * If iteration progress is 1 we count that as the end + * of the previous iteration. + */ + if (!iterationProgress && progress >= 1) { + iterationProgress = 1; + } + iterationProgress === 1 && currentIteration--; + currentIteration = Math.min(currentIteration, repeat + 1); + /** + * Reverse progress if we're not running in "normal" direction + */ + const isOddIteration = Boolean(currentIteration % 2); + if (isOddIteration) { + if (repeatType === "reverse") { + iterationProgress = 1 - iterationProgress; + if (repeatDelay) { + iterationProgress -= repeatDelay / resolvedDuration; + } + } + else if (repeatType === "mirror") { + frameGenerator = mirroredGenerator; + } + } + elapsed = clamp(0, 1, iterationProgress) * resolvedDuration; + } + /** + * If we're in negative time, set state as the initial keyframe. + * This prevents delay: x, duration: 0 animations from finishing + * instantly. + */ + const state = isInDelayPhase + ? { done: false, value: keyframes[0] } + : frameGenerator.next(elapsed); + if (mixKeyframes) { + state.value = mixKeyframes(state.value); + } + let { done } = state; + if (!isInDelayPhase && calculatedDuration !== null) { + done = + this.playbackSpeed >= 0 + ? this.currentTime >= totalDuration + : this.currentTime <= 0; + } + const isAnimationFinished = this.holdTime === null && + (this.state === "finished" || (this.state === "running" && done)); + // TODO: The exception for inertia could be cleaner here + if (isAnimationFinished && type !== inertia) { + state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed); + } + if (onUpdate) { + onUpdate(state.value); + } + if (isAnimationFinished) { + this.finish(); + } + return state; + } + /** + * Allows the returned animation to be awaited or promise-chained. Currently + * resolves when the animation finishes at all but in a future update could/should + * reject if its cancels. + */ + then(resolve, reject) { + return this.finished.then(resolve, reject); + } + get duration() { + return millisecondsToSeconds(this.calculatedDuration); + } + get time() { + return millisecondsToSeconds(this.currentTime); + } + set time(newTime) { + newTime = secondsToMilliseconds(newTime); + this.currentTime = newTime; + if (this.startTime === null || + this.holdTime !== null || + this.playbackSpeed === 0) { + this.holdTime = newTime; + } + else if (this.driver) { + this.startTime = this.driver.now() - newTime / this.playbackSpeed; + } + this.driver?.start(false); + } + get speed() { + return this.playbackSpeed; + } + set speed(newSpeed) { + this.updateTime(time.now()); + const hasChanged = this.playbackSpeed !== newSpeed; + this.playbackSpeed = newSpeed; + if (hasChanged) { + this.time = millisecondsToSeconds(this.currentTime); + } + } + play() { + if (this.isStopped) + return; + const { driver = frameloopDriver, startTime } = this.options; + if (!this.driver) { + this.driver = driver((timestamp) => this.tick(timestamp)); + } + this.options.onPlay?.(); + const now = this.driver.now(); + if (this.state === "finished") { + this.updateFinished(); + this.startTime = now; + } + else if (this.holdTime !== null) { + this.startTime = now - this.holdTime; + } + else if (!this.startTime) { + this.startTime = startTime ?? now; + } + if (this.state === "finished" && this.speed < 0) { + this.startTime += this.calculatedDuration; + } + this.holdTime = null; + /** + * Set playState to running only after we've used it in + * the previous logic. + */ + this.state = "running"; + this.driver.start(); + } + pause() { + this.state = "paused"; + this.updateTime(time.now()); + this.holdTime = this.currentTime; + } + complete() { + if (this.state !== "running") { + this.play(); + } + this.state = "finished"; + this.holdTime = null; + } + finish() { + this.notifyFinished(); + this.teardown(); + this.state = "finished"; + this.options.onComplete?.(); + } + cancel() { + this.holdTime = null; + this.startTime = 0; + this.tick(0); + this.teardown(); + this.options.onCancel?.(); + } + teardown() { + this.state = "idle"; + this.stopDriver(); + this.startTime = this.holdTime = null; + activeAnimations.mainThread--; + } + stopDriver() { + if (!this.driver) + return; + this.driver.stop(); + this.driver = undefined; + } + sample(sampleTime) { + this.startTime = 0; + return this.tick(sampleTime, true); + } + attachTimeline(timeline) { + if (this.options.allowFlatten) { + this.options.type = "keyframes"; + this.options.ease = "linear"; + this.initAnimation(); + } + this.driver?.stop(); + return timeline.observe(this); + } +} +// Legacy function support +function animateValue(options) { + return new JSAnimation(options); +} + +export { JSAnimation, animateValue }; diff --git a/node_modules/motion-dom/dist/es/animation/NativeAnimation.mjs b/node_modules/motion-dom/dist/es/animation/NativeAnimation.mjs new file mode 100644 index 00000000..60a12e2c --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/NativeAnimation.mjs @@ -0,0 +1,156 @@ +import { invariant, millisecondsToSeconds, secondsToMilliseconds, noop } from 'motion-utils'; +import { setStyle } from '../render/dom/style-set.mjs'; +import { supportsScrollTimeline } from '../utils/supports/scroll-timeline.mjs'; +import { getFinalKeyframe } from './keyframes/get-final.mjs'; +import { WithPromise } from './utils/WithPromise.mjs'; +import { startWaapiAnimation } from './waapi/start-waapi-animation.mjs'; +import { applyGeneratorOptions } from './waapi/utils/apply-generator.mjs'; + +/** + * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API. + */ +class NativeAnimation extends WithPromise { + constructor(options) { + super(); + this.finishedTime = null; + this.isStopped = false; + if (!options) + return; + const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options; + this.isPseudoElement = Boolean(pseudoElement); + this.allowFlatten = allowFlatten; + this.options = options; + invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`); + const transition = applyGeneratorOptions(options); + this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement); + if (transition.autoplay === false) { + this.animation.pause(); + } + this.animation.onfinish = () => { + this.finishedTime = this.time; + if (!pseudoElement) { + const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed); + if (this.updateMotionValue) { + this.updateMotionValue(keyframe); + } + else { + /** + * If we can, we want to commit the final style as set by the user, + * rather than the computed keyframe value supplied by the animation. + */ + setStyle(element, name, keyframe); + } + this.animation.cancel(); + } + onComplete?.(); + this.notifyFinished(); + }; + } + play() { + if (this.isStopped) + return; + this.animation.play(); + if (this.state === "finished") { + this.updateFinished(); + } + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.finish?.(); + } + cancel() { + try { + this.animation.cancel(); + } + catch (e) { } + } + stop() { + if (this.isStopped) + return; + this.isStopped = true; + const { state } = this; + if (state === "idle" || state === "finished") { + return; + } + if (this.updateMotionValue) { + this.updateMotionValue(); + } + else { + this.commitStyles(); + } + if (!this.isPseudoElement) + this.cancel(); + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * In this method, we commit styles back to the DOM before cancelling + * the animation. + * + * This is designed to be overridden by NativeAnimationExtended, which + * will create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to also correctly calculate velocity for any subsequent animation + * while deferring the commit until the next animation frame. + */ + commitStyles() { + if (!this.isPseudoElement) { + this.animation.commitStyles?.(); + } + } + get duration() { + const duration = this.animation.effect?.getComputedTiming?.().duration || 0; + return millisecondsToSeconds(Number(duration)); + } + get time() { + return millisecondsToSeconds(Number(this.animation.currentTime) || 0); + } + set time(newTime) { + this.finishedTime = null; + this.animation.currentTime = secondsToMilliseconds(newTime); + } + /** + * The playback speed of the animation. + * 1 = normal speed, 2 = double speed, 0.5 = half speed. + */ + get speed() { + return this.animation.playbackRate; + } + set speed(newSpeed) { + // Allow backwards playback after finishing + if (newSpeed < 0) + this.finishedTime = null; + this.animation.playbackRate = newSpeed; + } + get state() { + return this.finishedTime !== null + ? "finished" + : this.animation.playState; + } + get startTime() { + return Number(this.animation.startTime); + } + set startTime(newStartTime) { + this.animation.startTime = newStartTime; + } + /** + * Attaches a timeline to the animation, for instance the `ScrollTimeline`. + */ + attachTimeline({ timeline, observe }) { + if (this.allowFlatten) { + this.animation.effect?.updateTiming({ easing: "linear" }); + } + this.animation.onfinish = null; + if (timeline && supportsScrollTimeline()) { + this.animation.timeline = timeline; + return noop; + } + else { + return observe(this); + } + } +} + +export { NativeAnimation }; diff --git a/node_modules/motion-dom/dist/es/animation/NativeAnimationExtended.mjs b/node_modules/motion-dom/dist/es/animation/NativeAnimationExtended.mjs new file mode 100644 index 00000000..45a1b91c --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/NativeAnimationExtended.mjs @@ -0,0 +1,65 @@ +import { secondsToMilliseconds } from 'motion-utils'; +import { JSAnimation } from './JSAnimation.mjs'; +import { NativeAnimation } from './NativeAnimation.mjs'; +import { replaceTransitionType } from './utils/replace-transition-type.mjs'; +import { replaceStringEasing } from './waapi/utils/unsupported-easing.mjs'; + +/** + * 10ms is chosen here as it strikes a balance between smooth + * results (more than one keyframe per frame at 60fps) and + * keyframe quantity. + */ +const sampleDelta = 10; //ms +class NativeAnimationExtended extends NativeAnimation { + constructor(options) { + /** + * The base NativeAnimation function only supports a subset + * of Motion easings, and WAAPI also only supports some + * easing functions via string/cubic-bezier definitions. + * + * This function replaces those unsupported easing functions + * with a JS easing function. This will later get compiled + * to a linear() easing function. + */ + replaceStringEasing(options); + /** + * Ensure we replace the transition type with a generator function + * before passing to WAAPI. + * + * TODO: Does this have a better home? It could be shared with + * JSAnimation. + */ + replaceTransitionType(options); + super(options); + if (options.startTime) { + this.startTime = options.startTime; + } + this.options = options; + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * Rather than read commited styles back out of the DOM, we can + * create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to calculate velocity for any subsequent animation. + */ + updateMotionValue(value) { + const { motionValue, onUpdate, onComplete, element, ...options } = this.options; + if (!motionValue) + return; + if (value !== undefined) { + motionValue.set(value); + return; + } + const sampleAnimation = new JSAnimation({ + ...options, + autoplay: false, + }); + const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time); + motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta); + sampleAnimation.stop(); + } +} + +export { NativeAnimationExtended }; diff --git a/node_modules/motion-dom/dist/es/animation/NativeAnimationWrapper.mjs b/node_modules/motion-dom/dist/es/animation/NativeAnimationWrapper.mjs new file mode 100644 index 00000000..291f10d6 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/NativeAnimationWrapper.mjs @@ -0,0 +1,14 @@ +import { NativeAnimation } from './NativeAnimation.mjs'; + +class NativeAnimationWrapper extends NativeAnimation { + constructor(animation) { + super(); + this.animation = animation; + animation.onfinish = () => { + this.finishedTime = this.time; + this.notifyFinished(); + }; + } +} + +export { NativeAnimationWrapper }; diff --git a/node_modules/motion-dom/dist/es/animation/drivers/frame.mjs b/node_modules/motion-dom/dist/es/animation/drivers/frame.mjs new file mode 100644 index 00000000..a183b7cc --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/drivers/frame.mjs @@ -0,0 +1,17 @@ +import { time } from '../../frameloop/sync-time.mjs'; +import { frame, cancelFrame, frameData } from '../../frameloop/frame.mjs'; + +const frameloopDriver = (update) => { + const passTimestamp = ({ timestamp }) => update(timestamp); + return { + start: (keepAlive = true) => frame.update(passTimestamp, keepAlive), + stop: () => cancelFrame(passTimestamp), + /** + * If we're processing this frame we can use the + * framelocked timestamp to keep things in sync. + */ + now: () => (frameData.isProcessing ? frameData.timestamp : time.now()), + }; +}; + +export { frameloopDriver }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/inertia.mjs b/node_modules/motion-dom/dist/es/animation/generators/inertia.mjs new file mode 100644 index 00000000..b4b691b6 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/inertia.mjs @@ -0,0 +1,87 @@ +import { spring } from './spring/index.mjs'; +import { calcGeneratorVelocity } from './utils/velocity.mjs'; + +function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) { + const origin = keyframes[0]; + const state = { + done: false, + value: origin, + }; + const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max); + const nearestBoundary = (v) => { + if (min === undefined) + return max; + if (max === undefined) + return min; + return Math.abs(min - v) < Math.abs(max - v) ? min : max; + }; + let amplitude = power * velocity; + const ideal = origin + amplitude; + const target = modifyTarget === undefined ? ideal : modifyTarget(ideal); + /** + * If the target has changed we need to re-calculate the amplitude, otherwise + * the animation will start from the wrong position. + */ + if (target !== ideal) + amplitude = target - origin; + const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant); + const calcLatest = (t) => target + calcDelta(t); + const applyFriction = (t) => { + const delta = calcDelta(t); + const latest = calcLatest(t); + state.done = Math.abs(delta) <= restDelta; + state.value = state.done ? target : latest; + }; + /** + * Ideally this would resolve for t in a stateless way, we could + * do that by always precalculating the animation but as we know + * this will be done anyway we can assume that spring will + * be discovered during that. + */ + let timeReachedBoundary; + let spring$1; + const checkCatchBoundary = (t) => { + if (!isOutOfBounds(state.value)) + return; + timeReachedBoundary = t; + spring$1 = spring({ + keyframes: [state.value, nearestBoundary(state.value)], + velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000 + damping: bounceDamping, + stiffness: bounceStiffness, + restDelta, + restSpeed, + }); + }; + checkCatchBoundary(0); + return { + calculatedDuration: null, + next: (t) => { + /** + * We need to resolve the friction to figure out if we need a + * spring but we don't want to do this twice per frame. So here + * we flag if we updated for this frame and later if we did + * we can skip doing it again. + */ + let hasUpdatedFrame = false; + if (!spring$1 && timeReachedBoundary === undefined) { + hasUpdatedFrame = true; + applyFriction(t); + checkCatchBoundary(t); + } + /** + * If we have a spring and the provided t is beyond the moment the friction + * animation crossed the min/max boundary, use the spring. + */ + if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) { + return spring$1.next(t - timeReachedBoundary); + } + else { + !hasUpdatedFrame && applyFriction(t); + return state; + } + }, + }; +} + +export { inertia }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/keyframes.mjs b/node_modules/motion-dom/dist/es/animation/generators/keyframes.mjs new file mode 100644 index 00000000..2b6ec62b --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/keyframes.mjs @@ -0,0 +1,49 @@ +import { easeInOut, isEasingArray, easingDefinitionToFunction } from 'motion-utils'; +import { interpolate } from '../../utils/interpolate.mjs'; +import { defaultOffset } from '../keyframes/offsets/default.mjs'; +import { convertOffsetToTimes } from '../keyframes/offsets/time.mjs'; + +function defaultEasing(values, easing) { + return values.map(() => easing || easeInOut).splice(0, values.length - 1); +} +function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) { + /** + * Easing functions can be externally defined as strings. Here we convert them + * into actual functions. + */ + const easingFunctions = isEasingArray(ease) + ? ease.map(easingDefinitionToFunction) + : easingDefinitionToFunction(ease); + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { + done: false, + value: keyframeValues[0], + }; + /** + * Create a times array based on the provided 0-1 offsets + */ + const absoluteTimes = convertOffsetToTimes( + // Only use the provided offsets if they're the correct length + // TODO Maybe we should warn here if there's a length mismatch + times && times.length === keyframeValues.length + ? times + : defaultOffset(keyframeValues), duration); + const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, { + ease: Array.isArray(easingFunctions) + ? easingFunctions + : defaultEasing(keyframeValues, easingFunctions), + }); + return { + calculatedDuration: duration, + next: (t) => { + state.value = mapTimeToKeyframe(t); + state.done = t >= duration; + return state; + }, + }; +} + +export { defaultEasing, keyframes }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/spring/defaults.mjs b/node_modules/motion-dom/dist/es/animation/generators/spring/defaults.mjs new file mode 100644 index 00000000..3e9e4e47 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/spring/defaults.mjs @@ -0,0 +1,27 @@ +const springDefaults = { + // Default spring physics + stiffness: 100, + damping: 10, + mass: 1.0, + velocity: 0.0, + // Default duration/bounce-based options + duration: 800, // in ms + bounce: 0.3, + visualDuration: 0.3, // in seconds + // Rest thresholds + restSpeed: { + granular: 0.01, + default: 2, + }, + restDelta: { + granular: 0.005, + default: 0.5, + }, + // Limits + minDuration: 0.01, // in seconds + maxDuration: 10.0, // in seconds + minDamping: 0.05, + maxDamping: 1, +}; + +export { springDefaults }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/spring/find.mjs b/node_modules/motion-dom/dist/es/animation/generators/spring/find.mjs new file mode 100644 index 00000000..2229c438 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/spring/find.mjs @@ -0,0 +1,84 @@ +import { warning, secondsToMilliseconds, clamp, millisecondsToSeconds } from 'motion-utils'; +import { springDefaults } from './defaults.mjs'; + +const safeMin = 0.001; +function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) { + let envelope; + let derivative; + warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less"); + let dampingRatio = 1 - bounce; + /** + * Restrict dampingRatio and duration to within acceptable ranges. + */ + dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio); + duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration)); + if (dampingRatio < 1) { + /** + * Underdamped spring + */ + envelope = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const a = exponentialDecay - velocity; + const b = calcAngularFreq(undampedFreq, dampingRatio); + const c = Math.exp(-delta); + return safeMin - (a / b) * c; + }; + derivative = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const d = delta * velocity + velocity; + const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration; + const f = Math.exp(-delta); + const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio); + const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1; + return (factor * ((d - e) * f)) / g; + }; + } + else { + /** + * Critically-damped spring + */ + envelope = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (undampedFreq - velocity) * duration + 1; + return -safeMin + a * b; + }; + derivative = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (velocity - undampedFreq) * (duration * duration); + return a * b; + }; + } + const initialGuess = 5 / duration; + const undampedFreq = approximateRoot(envelope, derivative, initialGuess); + duration = secondsToMilliseconds(duration); + if (isNaN(undampedFreq)) { + return { + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + duration, + }; + } + else { + const stiffness = Math.pow(undampedFreq, 2) * mass; + return { + stiffness, + damping: dampingRatio * 2 * Math.sqrt(mass * stiffness), + duration, + }; + } +} +const rootIterations = 12; +function approximateRoot(envelope, derivative, initialGuess) { + let result = initialGuess; + for (let i = 1; i < rootIterations; i++) { + result = result - envelope(result) / derivative(result); + } + return result; +} +function calcAngularFreq(undampedFreq, dampingRatio) { + return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio); +} + +export { calcAngularFreq, findSpring }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/spring/index.mjs b/node_modules/motion-dom/dist/es/animation/generators/spring/index.mjs new file mode 100644 index 00000000..67e5dde9 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/spring/index.mjs @@ -0,0 +1,175 @@ +import { millisecondsToSeconds, secondsToMilliseconds, clamp } from 'motion-utils'; +import { generateLinearEasing } from '../../waapi/utils/linear.mjs'; +import { calcGeneratorDuration, maxGeneratorDuration } from '../utils/calc-duration.mjs'; +import { createGeneratorEasing } from '../utils/create-generator-easing.mjs'; +import { calcGeneratorVelocity } from '../utils/velocity.mjs'; +import { springDefaults } from './defaults.mjs'; +import { findSpring, calcAngularFreq } from './find.mjs'; + +const durationKeys = ["duration", "bounce"]; +const physicsKeys = ["stiffness", "damping", "mass"]; +function isSpringType(options, keys) { + return keys.some((key) => options[key] !== undefined); +} +function getSpringOptions(options) { + let springOptions = { + velocity: springDefaults.velocity, + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + mass: springDefaults.mass, + isResolvedFromDuration: false, + ...options, + }; + // stiffness/damping/mass overrides duration/bounce + if (!isSpringType(options, physicsKeys) && + isSpringType(options, durationKeys)) { + if (options.visualDuration) { + const visualDuration = options.visualDuration; + const root = (2 * Math.PI) / (visualDuration * 1.2); + const stiffness = root * root; + const damping = 2 * + clamp(0.05, 1, 1 - (options.bounce || 0)) * + Math.sqrt(stiffness); + springOptions = { + ...springOptions, + mass: springDefaults.mass, + stiffness, + damping, + }; + } + else { + const derived = findSpring(options); + springOptions = { + ...springOptions, + ...derived, + mass: springDefaults.mass, + }; + springOptions.isResolvedFromDuration = true; + } + } + return springOptions; +} +function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) { + const options = typeof optionsOrVisualDuration !== "object" + ? { + visualDuration: optionsOrVisualDuration, + keyframes: [0, 1], + bounce, + } + : optionsOrVisualDuration; + let { restSpeed, restDelta } = options; + const origin = options.keyframes[0]; + const target = options.keyframes[options.keyframes.length - 1]; + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { done: false, value: origin }; + const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({ + ...options, + velocity: -millisecondsToSeconds(options.velocity || 0), + }); + const initialVelocity = velocity || 0.0; + const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass)); + const initialDelta = target - origin; + const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass)); + /** + * If we're working on a granular scale, use smaller defaults for determining + * when the spring is finished. + * + * These defaults have been selected emprically based on what strikes a good + * ratio between feeling good and finishing as soon as changes are imperceptible. + */ + const isGranularScale = Math.abs(initialDelta) < 5; + restSpeed || (restSpeed = isGranularScale + ? springDefaults.restSpeed.granular + : springDefaults.restSpeed.default); + restDelta || (restDelta = isGranularScale + ? springDefaults.restDelta.granular + : springDefaults.restDelta.default); + let resolveSpring; + if (dampingRatio < 1) { + const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio); + // Underdamped spring + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + return (target - + envelope * + (((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) / + angularFreq) * + Math.sin(angularFreq * t) + + initialDelta * Math.cos(angularFreq * t))); + }; + } + else if (dampingRatio === 1) { + // Critically damped spring + resolveSpring = (t) => target - + Math.exp(-undampedAngularFreq * t) * + (initialDelta + + (initialVelocity + undampedAngularFreq * initialDelta) * t); + } + else { + // Overdamped spring + const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1); + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + // When performing sinh or cosh values can hit Infinity so we cap them here + const freqForT = Math.min(dampedAngularFreq * t, 300); + return (target - + (envelope * + ((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) * + Math.sinh(freqForT) + + dampedAngularFreq * + initialDelta * + Math.cosh(freqForT))) / + dampedAngularFreq); + }; + } + const generator = { + calculatedDuration: isResolvedFromDuration ? duration || null : null, + next: (t) => { + const current = resolveSpring(t); + if (!isResolvedFromDuration) { + let currentVelocity = t === 0 ? initialVelocity : 0.0; + /** + * We only need to calculate velocity for under-damped springs + * as over- and critically-damped springs can't overshoot, so + * checking only for displacement is enough. + */ + if (dampingRatio < 1) { + currentVelocity = + t === 0 + ? secondsToMilliseconds(initialVelocity) + : calcGeneratorVelocity(resolveSpring, t, current); + } + const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed; + const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta; + state.done = + isBelowVelocityThreshold && isBelowDisplacementThreshold; + } + else { + state.done = t >= duration; + } + state.value = state.done ? target : current; + return state; + }, + toString: () => { + const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30); + return calculatedDuration + "ms " + easing; + }, + toTransition: () => { }, + }; + return generator; +} +spring.applyToOptions = (options) => { + const generatorOptions = createGeneratorEasing(options, 100, spring); + options.ease = generatorOptions.ease; + options.duration = secondsToMilliseconds(generatorOptions.duration); + options.type = "keyframes"; + return options; +}; + +export { spring }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/utils/calc-duration.mjs b/node_modules/motion-dom/dist/es/animation/generators/utils/calc-duration.mjs new file mode 100644 index 00000000..a00f1639 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/utils/calc-duration.mjs @@ -0,0 +1,17 @@ +/** + * Implement a practical max duration for keyframe generation + * to prevent infinite loops + */ +const maxGeneratorDuration = 20000; +function calcGeneratorDuration(generator) { + let duration = 0; + const timeStep = 50; + let state = generator.next(duration); + while (!state.done && duration < maxGeneratorDuration) { + duration += timeStep; + state = generator.next(duration); + } + return duration >= maxGeneratorDuration ? Infinity : duration; +} + +export { calcGeneratorDuration, maxGeneratorDuration }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/utils/create-generator-easing.mjs b/node_modules/motion-dom/dist/es/animation/generators/utils/create-generator-easing.mjs new file mode 100644 index 00000000..97093943 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/utils/create-generator-easing.mjs @@ -0,0 +1,19 @@ +import { millisecondsToSeconds } from 'motion-utils'; +import { calcGeneratorDuration, maxGeneratorDuration } from './calc-duration.mjs'; + +/** + * Create a progress => progress easing function from a generator. + */ +function createGeneratorEasing(options, scale = 100, createGenerator) { + const generator = createGenerator({ ...options, keyframes: [0, scale] }); + const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + return { + type: "keyframes", + ease: (progress) => { + return generator.next(duration * progress).value / scale; + }, + duration: millisecondsToSeconds(duration), + }; +} + +export { createGeneratorEasing }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/utils/is-generator.mjs b/node_modules/motion-dom/dist/es/animation/generators/utils/is-generator.mjs new file mode 100644 index 00000000..e7a725a8 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/utils/is-generator.mjs @@ -0,0 +1,5 @@ +function isGenerator(type) { + return typeof type === "function" && "applyToOptions" in type; +} + +export { isGenerator }; diff --git a/node_modules/motion-dom/dist/es/animation/generators/utils/velocity.mjs b/node_modules/motion-dom/dist/es/animation/generators/utils/velocity.mjs new file mode 100644 index 00000000..3f5b3b19 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/generators/utils/velocity.mjs @@ -0,0 +1,9 @@ +import { velocityPerSecond } from 'motion-utils'; + +const velocitySampleDuration = 5; // ms +function calcGeneratorVelocity(resolveValue, t, current) { + const prevT = Math.max(t - velocitySampleDuration, 0); + return velocityPerSecond(current - resolveValue(prevT), t - prevT); +} + +export { calcGeneratorVelocity }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/DOMKeyframesResolver.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/DOMKeyframesResolver.mjs new file mode 100644 index 00000000..92b84f2d --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/DOMKeyframesResolver.mjs @@ -0,0 +1,131 @@ +import { positionalKeys } from '../../render/utils/keys-position.mjs'; +import { findDimensionValueType } from '../../value/types/dimensions.mjs'; +import { getVariableValue } from '../utils/css-variables-conversion.mjs'; +import { isCSSVariableToken } from '../utils/is-css-variable.mjs'; +import { KeyframeResolver } from './KeyframesResolver.mjs'; +import { isNone } from './utils/is-none.mjs'; +import { makeNoneKeyframesAnimatable } from './utils/make-none-animatable.mjs'; +import { isNumOrPxType, positionalValues } from './utils/unit-conversion.mjs'; + +class DOMKeyframesResolver extends KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element) { + super(unresolvedKeyframes, onComplete, name, motionValue, element, true); + } + readKeyframes() { + const { unresolvedKeyframes, element, name } = this; + if (!element || !element.current) + return; + super.readKeyframes(); + /** + * If any keyframe is a CSS variable, we need to find its value by sampling the element + */ + for (let i = 0; i < unresolvedKeyframes.length; i++) { + let keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string") { + keyframe = keyframe.trim(); + if (isCSSVariableToken(keyframe)) { + const resolved = getVariableValue(keyframe, element.current); + if (resolved !== undefined) { + unresolvedKeyframes[i] = resolved; + } + if (i === unresolvedKeyframes.length - 1) { + this.finalKeyframe = keyframe; + } + } + } + } + /** + * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes. + * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which + * have a far bigger performance impact. + */ + this.resolveNoneKeyframes(); + /** + * Check to see if unit type has changed. If so schedule jobs that will + * temporarily set styles to the destination keyframes. + * Skip if we have more than two keyframes or this isn't a positional value. + * TODO: We can throw if there are multiple keyframes and the value type changes. + */ + if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) { + return; + } + const [origin, target] = unresolvedKeyframes; + const originType = findDimensionValueType(origin); + const targetType = findDimensionValueType(target); + /** + * Either we don't recognise these value types or we can animate between them. + */ + if (originType === targetType) + return; + /** + * If both values are numbers or pixels, we can animate between them by + * converting them to numbers. + */ + if (isNumOrPxType(originType) && isNumOrPxType(targetType)) { + for (let i = 0; i < unresolvedKeyframes.length; i++) { + const value = unresolvedKeyframes[i]; + if (typeof value === "string") { + unresolvedKeyframes[i] = parseFloat(value); + } + } + } + else if (positionalValues[name]) { + /** + * Else, the only way to resolve this is by measuring the element. + */ + this.needsMeasurement = true; + } + } + resolveNoneKeyframes() { + const { unresolvedKeyframes, name } = this; + const noneKeyframeIndexes = []; + for (let i = 0; i < unresolvedKeyframes.length; i++) { + if (unresolvedKeyframes[i] === null || + isNone(unresolvedKeyframes[i])) { + noneKeyframeIndexes.push(i); + } + } + if (noneKeyframeIndexes.length) { + makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name); + } + } + measureInitialState() { + const { element, unresolvedKeyframes, name } = this; + if (!element || !element.current) + return; + if (name === "height") { + this.suspendedScrollY = window.pageYOffset; + } + this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + unresolvedKeyframes[0] = this.measuredOrigin; + // Set final key frame to measure after next render + const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (measureKeyframe !== undefined) { + element.getValue(name, measureKeyframe).jump(measureKeyframe, false); + } + } + measureEndState() { + const { element, name, unresolvedKeyframes } = this; + if (!element || !element.current) + return; + const value = element.getValue(name); + value && value.jump(this.measuredOrigin, false); + const finalKeyframeIndex = unresolvedKeyframes.length - 1; + const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex]; + unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + if (finalKeyframe !== null && this.finalKeyframe === undefined) { + this.finalKeyframe = finalKeyframe; + } + // If we removed transform values, reapply them before the next render + if (this.removedTransforms?.length) { + this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => { + element + .getValue(unsetTransformName) + .set(unsetTransformValue); + }); + } + this.resolveNoneKeyframes(); + } +} + +export { DOMKeyframesResolver }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/KeyframesResolver.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/KeyframesResolver.mjs new file mode 100644 index 00000000..feaa7672 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/KeyframesResolver.mjs @@ -0,0 +1,147 @@ +import { fillWildcards } from './utils/fill-wildcards.mjs'; +import { removeNonTranslationalTransform } from './utils/unit-conversion.mjs'; +import { frame } from '../../frameloop/frame.mjs'; + +const toResolve = new Set(); +let isScheduled = false; +let anyNeedsMeasurement = false; +let isForced = false; +function measureAllKeyframes() { + if (anyNeedsMeasurement) { + const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement); + const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element)); + const transformsToRestore = new Map(); + /** + * Write pass + * If we're measuring elements we want to remove bounding box-changing transforms. + */ + elementsToMeasure.forEach((element) => { + const removedTransforms = removeNonTranslationalTransform(element); + if (!removedTransforms.length) + return; + transformsToRestore.set(element, removedTransforms); + element.render(); + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureInitialState()); + // Write + elementsToMeasure.forEach((element) => { + element.render(); + const restore = transformsToRestore.get(element); + if (restore) { + restore.forEach(([key, value]) => { + element.getValue(key)?.set(value); + }); + } + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureEndState()); + // Write + resolversToMeasure.forEach((resolver) => { + if (resolver.suspendedScrollY !== undefined) { + window.scrollTo(0, resolver.suspendedScrollY); + } + }); + } + anyNeedsMeasurement = false; + isScheduled = false; + toResolve.forEach((resolver) => resolver.complete(isForced)); + toResolve.clear(); +} +function readAllKeyframes() { + toResolve.forEach((resolver) => { + resolver.readKeyframes(); + if (resolver.needsMeasurement) { + anyNeedsMeasurement = true; + } + }); +} +function flushKeyframeResolvers() { + isForced = true; + readAllKeyframes(); + measureAllKeyframes(); + isForced = false; +} +class KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) { + this.state = "pending"; + /** + * Track whether this resolver is async. If it is, it'll be added to the + * resolver queue and flushed in the next frame. Resolvers that aren't going + * to trigger read/write thrashing don't need to be async. + */ + this.isAsync = false; + /** + * Track whether this resolver needs to perform a measurement + * to resolve its keyframes. + */ + this.needsMeasurement = false; + this.unresolvedKeyframes = [...unresolvedKeyframes]; + this.onComplete = onComplete; + this.name = name; + this.motionValue = motionValue; + this.element = element; + this.isAsync = isAsync; + } + scheduleResolve() { + this.state = "scheduled"; + if (this.isAsync) { + toResolve.add(this); + if (!isScheduled) { + isScheduled = true; + frame.read(readAllKeyframes); + frame.resolveKeyframes(measureAllKeyframes); + } + } + else { + this.readKeyframes(); + this.complete(); + } + } + readKeyframes() { + const { unresolvedKeyframes, name, element, motionValue } = this; + // If initial keyframe is null we need to read it from the DOM + if (unresolvedKeyframes[0] === null) { + const currentValue = motionValue?.get(); + // TODO: This doesn't work if the final keyframe is a wildcard + const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (currentValue !== undefined) { + unresolvedKeyframes[0] = currentValue; + } + else if (element && name) { + const valueAsRead = element.readValue(name, finalKeyframe); + if (valueAsRead !== undefined && valueAsRead !== null) { + unresolvedKeyframes[0] = valueAsRead; + } + } + if (unresolvedKeyframes[0] === undefined) { + unresolvedKeyframes[0] = finalKeyframe; + } + if (motionValue && currentValue === undefined) { + motionValue.set(unresolvedKeyframes[0]); + } + } + fillWildcards(unresolvedKeyframes); + } + setFinalKeyframe() { } + measureInitialState() { } + renderEndStyles() { } + measureEndState() { } + complete(isForcedComplete = false) { + this.state = "complete"; + this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForcedComplete); + toResolve.delete(this); + } + cancel() { + if (this.state === "scheduled") { + toResolve.delete(this); + this.state = "pending"; + } + } + resume() { + if (this.state === "pending") + this.scheduleResolve(); + } +} + +export { KeyframeResolver, flushKeyframeResolvers }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/get-final.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/get-final.mjs new file mode 100644 index 00000000..b0e1829e --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/get-final.mjs @@ -0,0 +1,11 @@ +const isNotNull = (value) => value !== null; +function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1); + const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; +} + +export { getFinalKeyframe }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/offsets/default.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/offsets/default.mjs new file mode 100644 index 00000000..fc45c6d7 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/offsets/default.mjs @@ -0,0 +1,9 @@ +import { fillOffset } from './fill.mjs'; + +function defaultOffset(arr) { + const offset = [0]; + fillOffset(offset, arr.length - 1); + return offset; +} + +export { defaultOffset }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/offsets/fill.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/offsets/fill.mjs new file mode 100644 index 00000000..e1ec3c40 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/offsets/fill.mjs @@ -0,0 +1,12 @@ +import { progress } from 'motion-utils'; +import { mixNumber } from '../../../utils/mix/number.mjs'; + +function fillOffset(offset, remaining) { + const min = offset[offset.length - 1]; + for (let i = 1; i <= remaining; i++) { + const offsetProgress = progress(0, remaining, i); + offset.push(mixNumber(min, 1, offsetProgress)); + } +} + +export { fillOffset }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/offsets/time.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/offsets/time.mjs new file mode 100644 index 00000000..fa62db71 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/offsets/time.mjs @@ -0,0 +1,5 @@ +function convertOffsetToTimes(offset, duration) { + return offset.map((o) => o * duration); +} + +export { convertOffsetToTimes }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/utils/apply-px-defaults.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/utils/apply-px-defaults.mjs new file mode 100644 index 00000000..4629fb94 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/utils/apply-px-defaults.mjs @@ -0,0 +1,11 @@ +import { pxValues } from '../../waapi/utils/px-values.mjs'; + +function applyPxDefaults(keyframes, name) { + for (let i = 0; i < keyframes.length; i++) { + if (typeof keyframes[i] === "number" && pxValues.has(name)) { + keyframes[i] = keyframes[i] + "px"; + } + } +} + +export { applyPxDefaults }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/utils/fill-wildcards.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/utils/fill-wildcards.mjs new file mode 100644 index 00000000..c9421d78 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/utils/fill-wildcards.mjs @@ -0,0 +1,7 @@ +function fillWildcards(keyframes) { + for (let i = 1; i < keyframes.length; i++) { + keyframes[i] ?? (keyframes[i] = keyframes[i - 1]); + } +} + +export { fillWildcards }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/utils/is-none.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/utils/is-none.mjs new file mode 100644 index 00000000..28253e93 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/utils/is-none.mjs @@ -0,0 +1,15 @@ +import { isZeroValueString } from 'motion-utils'; + +function isNone(value) { + if (typeof value === "number") { + return value === 0; + } + else if (value !== null) { + return value === "none" || value === "0" || isZeroValueString(value); + } + else { + return true; + } +} + +export { isNone }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/utils/make-none-animatable.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/utils/make-none-animatable.mjs new file mode 100644 index 00000000..f6aafd83 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/utils/make-none-animatable.mjs @@ -0,0 +1,30 @@ +import { analyseComplexValue } from '../../../value/types/complex/index.mjs'; +import { getAnimatableNone } from '../../../value/types/utils/animatable-none.mjs'; + +/** + * If we encounter keyframes like "none" or "0" and we also have keyframes like + * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for + * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into + * zero equivalents, i.e. "#fff0" or "0px 0px". + */ +const invalidTemplates = new Set(["auto", "none", "0"]); +function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) { + let i = 0; + let animatableTemplate = undefined; + while (i < unresolvedKeyframes.length && !animatableTemplate) { + const keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string" && + !invalidTemplates.has(keyframe) && + analyseComplexValue(keyframe).values.length) { + animatableTemplate = unresolvedKeyframes[i]; + } + i++; + } + if (animatableTemplate && name) { + for (const noneIndex of noneKeyframeIndexes) { + unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate); + } + } +} + +export { makeNoneKeyframesAnimatable }; diff --git a/node_modules/motion-dom/dist/es/animation/keyframes/utils/unit-conversion.mjs b/node_modules/motion-dom/dist/es/animation/keyframes/utils/unit-conversion.mjs new file mode 100644 index 00000000..bc07f717 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/keyframes/utils/unit-conversion.mjs @@ -0,0 +1,36 @@ +import { parseValueFromTransform } from '../../../render/dom/parse-transform.mjs'; +import { transformPropOrder } from '../../../render/utils/keys-transform.mjs'; +import { number } from '../../../value/types/numbers/index.mjs'; +import { px } from '../../../value/types/numbers/units.mjs'; + +const isNumOrPxType = (v) => v === number || v === px; +const transformKeys = new Set(["x", "y", "z"]); +const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key)); +function removeNonTranslationalTransform(visualElement) { + const removedTransforms = []; + nonTranslationalTransformKeys.forEach((key) => { + const value = visualElement.getValue(key); + if (value !== undefined) { + removedTransforms.push([key, value.get()]); + value.set(key.startsWith("scale") ? 1 : 0); + } + }); + return removedTransforms; +} +const positionalValues = { + // Dimensions + width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight), + height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom), + top: (_bbox, { top }) => parseFloat(top), + left: (_bbox, { left }) => parseFloat(left), + bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min), + right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min), + // Transform + x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"), + y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"), +}; +// Alias translate longform names +positionalValues.translateX = positionalValues.x; +positionalValues.translateY = positionalValues.y; + +export { isNumOrPxType, positionalValues, removeNonTranslationalTransform }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/WithPromise.mjs b/node_modules/motion-dom/dist/es/animation/utils/WithPromise.mjs new file mode 100644 index 00000000..36dc80b8 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/WithPromise.mjs @@ -0,0 +1,26 @@ +class WithPromise { + constructor() { + this.updateFinished(); + } + get finished() { + return this._finished; + } + updateFinished() { + this._finished = new Promise((resolve) => { + this.resolve = resolve; + }); + } + notifyFinished() { + this.resolve(); + } + /** + * Allows the animation to be awaited. + * + * @deprecated Use `finished` instead. + */ + then(onResolve, onReject) { + return this.finished.then(onResolve, onReject); + } +} + +export { WithPromise }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/active-animations.mjs b/node_modules/motion-dom/dist/es/animation/utils/active-animations.mjs new file mode 100644 index 00000000..a8f8465d --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/active-animations.mjs @@ -0,0 +1,9 @@ +const animationMaps = new WeakMap(); +const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`; +function getAnimationMap(element) { + const map = animationMaps.get(element) || new Map(); + animationMaps.set(element, map); + return map; +} + +export { animationMapKey, getAnimationMap }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/can-animate.mjs b/node_modules/motion-dom/dist/es/animation/utils/can-animate.mjs new file mode 100644 index 00000000..85e546ee --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/can-animate.mjs @@ -0,0 +1,42 @@ +import { warning } from 'motion-utils'; +import { isGenerator } from '../generators/utils/is-generator.mjs'; +import { isAnimatable } from './is-animatable.mjs'; + +function hasKeyframesChanged(keyframes) { + const current = keyframes[0]; + if (keyframes.length === 1) + return true; + for (let i = 0; i < keyframes.length; i++) { + if (keyframes[i] !== current) + return true; + } +} +function canAnimate(keyframes, name, type, velocity) { + /** + * Check if we're able to animate between the start and end keyframes, + * and throw a warning if we're attempting to animate between one that's + * animatable and another that isn't. + */ + const originKeyframe = keyframes[0]; + if (originKeyframe === null) + return false; + /** + * These aren't traditionally animatable but we do support them. + * In future we could look into making this more generic or replacing + * this function with mix() === mixImmediate + */ + if (name === "display" || name === "visibility") + return true; + const targetKeyframe = keyframes[keyframes.length - 1]; + const isOriginAnimatable = isAnimatable(originKeyframe, name); + const isTargetAnimatable = isAnimatable(targetKeyframe, name); + warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`); + // Always skip if any of these are true + if (!isOriginAnimatable || !isTargetAnimatable) { + return false; + } + return (hasKeyframesChanged(keyframes) || + ((type === "spring" || isGenerator(type)) && velocity)); +} + +export { canAnimate }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/css-variables-conversion.mjs b/node_modules/motion-dom/dist/es/animation/utils/css-variables-conversion.mjs new file mode 100644 index 00000000..01fc4b0e --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/css-variables-conversion.mjs @@ -0,0 +1,41 @@ +import { invariant, isNumericalString } from 'motion-utils'; +import { isCSSVariableToken } from './is-css-variable.mjs'; + +/** + * Parse Framer's special CSS variable format into a CSS token and a fallback. + * + * ``` + * `var(--foo, #fff)` => [`--foo`, '#fff'] + * ``` + * + * @param current + */ +const splitCSSVariableRegex = +// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words +/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u; +function parseCSSVariable(current) { + const match = splitCSSVariableRegex.exec(current); + if (!match) + return [,]; + const [, token1, token2, fallback] = match; + return [`--${token1 ?? token2}`, fallback]; +} +const maxDepth = 4; +function getVariableValue(current, element, depth = 1) { + invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`); + const [token, fallback] = parseCSSVariable(current); + // No CSS variable detected + if (!token) + return; + // Attempt to read this CSS variable off the element + const resolved = window.getComputedStyle(element).getPropertyValue(token); + if (resolved) { + const trimmed = resolved.trim(); + return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed; + } + return isCSSVariableToken(fallback) + ? getVariableValue(fallback, element, depth + 1) + : fallback; +} + +export { getVariableValue, parseCSSVariable }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/get-value-transition.mjs b/node_modules/motion-dom/dist/es/animation/utils/get-value-transition.mjs new file mode 100644 index 00000000..20b3436b --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/get-value-transition.mjs @@ -0,0 +1,7 @@ +function getValueTransition(transition, key) { + return (transition?.[key] ?? + transition?.["default"] ?? + transition); +} + +export { getValueTransition }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/is-animatable.mjs b/node_modules/motion-dom/dist/es/animation/utils/is-animatable.mjs new file mode 100644 index 00000000..fc715e52 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/is-animatable.mjs @@ -0,0 +1,30 @@ +import { complex } from '../../value/types/complex/index.mjs'; + +/** + * Check if a value is animatable. Examples: + * + * ✅: 100, "100px", "#fff" + * ❌: "block", "url(2.jpg)" + * @param value + * + * @internal + */ +const isAnimatable = (value, name) => { + // If the list of keys tat might be non-animatable grows, replace with Set + if (name === "zIndex") + return false; + // If it's a number or a keyframes array, we can animate it. We might at some point + // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this, + // but for now lets leave it like this for performance reasons + if (typeof value === "number" || Array.isArray(value)) + return true; + if (typeof value === "string" && // It's animatable if we have a string + (complex.test(value) || value === "0") && // And it contains numbers and/or colors + !value.startsWith("url(") // Unless it starts with "url(" + ) { + return true; + } + return false; +}; + +export { isAnimatable }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/is-css-variable.mjs b/node_modules/motion-dom/dist/es/animation/utils/is-css-variable.mjs new file mode 100644 index 00000000..2abcd8ec --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/is-css-variable.mjs @@ -0,0 +1,15 @@ +const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token); +const isCSSVariableName = +/*@__PURE__*/ checkStringStartsWith("--"); +const startsAsVariableToken = +/*@__PURE__*/ checkStringStartsWith("var(--"); +const isCSSVariableToken = (value) => { + const startsWithToken = startsAsVariableToken(value); + if (!startsWithToken) + return false; + // Ensure any comments are stripped from the value as this can harm performance of the regex. + return singleCssVariableRegex.test(value.split("/*")[0].trim()); +}; +const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu; + +export { isCSSVariableName, isCSSVariableToken }; diff --git a/node_modules/motion-dom/dist/es/animation/utils/replace-transition-type.mjs b/node_modules/motion-dom/dist/es/animation/utils/replace-transition-type.mjs new file mode 100644 index 00000000..3cf59408 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/utils/replace-transition-type.mjs @@ -0,0 +1,18 @@ +import { inertia } from '../generators/inertia.mjs'; +import { keyframes } from '../generators/keyframes.mjs'; +import { spring } from '../generators/spring/index.mjs'; + +const transitionTypeMap = { + decay: inertia, + inertia, + tween: keyframes, + keyframes: keyframes, + spring, +}; +function replaceTransitionType(transition) { + if (typeof transition.type === "string") { + transition.type = transitionTypeMap[transition.type]; + } +} + +export { replaceTransitionType }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/easing/cubic-bezier.mjs b/node_modules/motion-dom/dist/es/animation/waapi/easing/cubic-bezier.mjs new file mode 100644 index 00000000..ebbdc68e --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/easing/cubic-bezier.mjs @@ -0,0 +1,3 @@ +const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`; + +export { cubicBezierAsString }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/easing/is-supported.mjs b/node_modules/motion-dom/dist/es/animation/waapi/easing/is-supported.mjs new file mode 100644 index 00000000..3de10e26 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/easing/is-supported.mjs @@ -0,0 +1,14 @@ +import { isBezierDefinition } from 'motion-utils'; +import { supportsLinearEasing } from '../../../utils/supports/linear-easing.mjs'; +import { supportedWaapiEasing } from './supported.mjs'; + +function isWaapiSupportedEasing(easing) { + return Boolean((typeof easing === "function" && supportsLinearEasing()) || + !easing || + (typeof easing === "string" && + (easing in supportedWaapiEasing || supportsLinearEasing())) || + isBezierDefinition(easing) || + (Array.isArray(easing) && easing.every(isWaapiSupportedEasing))); +} + +export { isWaapiSupportedEasing }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/easing/map-easing.mjs b/node_modules/motion-dom/dist/es/animation/waapi/easing/map-easing.mjs new file mode 100644 index 00000000..7fe1e47e --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/easing/map-easing.mjs @@ -0,0 +1,28 @@ +import { isBezierDefinition } from 'motion-utils'; +import { supportsLinearEasing } from '../../../utils/supports/linear-easing.mjs'; +import { generateLinearEasing } from '../utils/linear.mjs'; +import { cubicBezierAsString } from './cubic-bezier.mjs'; +import { supportedWaapiEasing } from './supported.mjs'; + +function mapEasingToNativeEasing(easing, duration) { + if (!easing) { + return undefined; + } + else if (typeof easing === "function") { + return supportsLinearEasing() + ? generateLinearEasing(easing, duration) + : "ease-out"; + } + else if (isBezierDefinition(easing)) { + return cubicBezierAsString(easing); + } + else if (Array.isArray(easing)) { + return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) || + supportedWaapiEasing.easeOut); + } + else { + return supportedWaapiEasing[easing]; + } +} + +export { mapEasingToNativeEasing }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/easing/supported.mjs b/node_modules/motion-dom/dist/es/animation/waapi/easing/supported.mjs new file mode 100644 index 00000000..258d81b5 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/easing/supported.mjs @@ -0,0 +1,15 @@ +import { cubicBezierAsString } from './cubic-bezier.mjs'; + +const supportedWaapiEasing = { + linear: "linear", + ease: "ease", + easeIn: "ease-in", + easeOut: "ease-out", + easeInOut: "ease-in-out", + circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]), + circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]), + backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]), + backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]), +}; + +export { supportedWaapiEasing }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/start-waapi-animation.mjs b/node_modules/motion-dom/dist/es/animation/waapi/start-waapi-animation.mjs new file mode 100644 index 00000000..5fd6f911 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/start-waapi-animation.mjs @@ -0,0 +1,39 @@ +import { activeAnimations } from '../../stats/animation-count.mjs'; +import { statsBuffer } from '../../stats/buffer.mjs'; +import { mapEasingToNativeEasing } from './easing/map-easing.mjs'; + +function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) { + const keyframeOptions = { + [valueName]: keyframes, + }; + if (times) + keyframeOptions.offset = times; + const easing = mapEasingToNativeEasing(ease, duration); + /** + * If this is an easing array, apply to keyframes, not animation as a whole + */ + if (Array.isArray(easing)) + keyframeOptions.easing = easing; + if (statsBuffer.value) { + activeAnimations.waapi++; + } + const options = { + delay, + duration, + easing: !Array.isArray(easing) ? easing : "linear", + fill: "both", + iterations: repeat + 1, + direction: repeatType === "reverse" ? "alternate" : "normal", + }; + if (pseudoElement) + options.pseudoElement = pseudoElement; + const animation = element.animate(keyframeOptions, options); + if (statsBuffer.value) { + animation.finished.finally(() => { + activeAnimations.waapi--; + }); + } + return animation; +} + +export { startWaapiAnimation }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/supports/partial-keyframes.mjs b/node_modules/motion-dom/dist/es/animation/waapi/supports/partial-keyframes.mjs new file mode 100644 index 00000000..480d0626 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/supports/partial-keyframes.mjs @@ -0,0 +1,13 @@ +import { memo } from 'motion-utils'; + +const supportsPartialKeyframes = /*@__PURE__*/ memo(() => { + try { + document.createElement("div").animate({ opacity: [1] }); + } + catch (e) { + return false; + } + return true; +}); + +export { supportsPartialKeyframes }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/supports/waapi.mjs b/node_modules/motion-dom/dist/es/animation/waapi/supports/waapi.mjs new file mode 100644 index 00000000..2f5b54fd --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/supports/waapi.mjs @@ -0,0 +1,37 @@ +import { memo } from 'motion-utils'; +import { isHTMLElement } from '../../../utils/is-html-element.mjs'; + +/** + * A list of values that can be hardware-accelerated. + */ +const acceleratedValues = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Could be re-enabled now we have support for linear() easing + // "background-color" +]); +const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate")); +function supportsBrowserAnimation(options) { + const { motionValue, name, repeatDelay, repeatType, damping, type } = options; + if (!isHTMLElement(motionValue?.owner?.current)) { + return false; + } + const { onUpdate, transformTemplate } = motionValue.owner.getProps(); + return (supportsWaapi() && + name && + acceleratedValues.has(name) && + (name !== "transform" || !transformTemplate) && + /** + * If we're outputting values to onUpdate then we can't use WAAPI as there's + * no way to read the value from WAAPI every frame. + */ + !onUpdate && + !repeatDelay && + repeatType !== "mirror" && + damping !== 0 && + type !== "inertia"); +} + +export { supportsBrowserAnimation }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs b/node_modules/motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs new file mode 100644 index 00000000..0b602b32 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs @@ -0,0 +1,14 @@ +/** + * A list of values that can be hardware-accelerated. + */ +const acceleratedValues = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved + // or until we implement support for linear() easing. + // "background-color" +]); + +export { acceleratedValues }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/utils/apply-generator.mjs b/node_modules/motion-dom/dist/es/animation/waapi/utils/apply-generator.mjs new file mode 100644 index 00000000..1e1c8d5e --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/utils/apply-generator.mjs @@ -0,0 +1,15 @@ +import { supportsLinearEasing } from '../../../utils/supports/linear-easing.mjs'; +import { isGenerator } from '../../generators/utils/is-generator.mjs'; + +function applyGeneratorOptions({ type, ...options }) { + if (isGenerator(type) && supportsLinearEasing()) { + return type.applyToOptions(options); + } + else { + options.duration ?? (options.duration = 300); + options.ease ?? (options.ease = "easeOut"); + } + return options; +} + +export { applyGeneratorOptions }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/utils/linear.mjs b/node_modules/motion-dom/dist/es/animation/waapi/utils/linear.mjs new file mode 100644 index 00000000..afc5a7f5 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/utils/linear.mjs @@ -0,0 +1,12 @@ +const generateLinearEasing = (easing, duration, // as milliseconds +resolution = 10 // as milliseconds +) => { + let points = ""; + const numPoints = Math.max(Math.round(duration / resolution), 2); + for (let i = 0; i < numPoints; i++) { + points += Math.round(easing(i / (numPoints - 1)) * 10000) / 10000 + ", "; + } + return `linear(${points.substring(0, points.length - 2)})`; +}; + +export { generateLinearEasing }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/utils/px-values.mjs b/node_modules/motion-dom/dist/es/animation/waapi/utils/px-values.mjs new file mode 100644 index 00000000..96b3b7a7 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/utils/px-values.mjs @@ -0,0 +1,39 @@ +const pxValues = new Set([ + // Border props + "borderWidth", + "borderTopWidth", + "borderRightWidth", + "borderBottomWidth", + "borderLeftWidth", + "borderRadius", + "radius", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + // Positioning props + "width", + "maxWidth", + "height", + "maxHeight", + "top", + "right", + "bottom", + "left", + // Spacing props + "padding", + "paddingTop", + "paddingRight", + "paddingBottom", + "paddingLeft", + "margin", + "marginTop", + "marginRight", + "marginBottom", + "marginLeft", + // Misc + "backgroundPositionX", + "backgroundPositionY", +]); + +export { pxValues }; diff --git a/node_modules/motion-dom/dist/es/animation/waapi/utils/unsupported-easing.mjs b/node_modules/motion-dom/dist/es/animation/waapi/utils/unsupported-easing.mjs new file mode 100644 index 00000000..bba31c20 --- /dev/null +++ b/node_modules/motion-dom/dist/es/animation/waapi/utils/unsupported-easing.mjs @@ -0,0 +1,18 @@ +import { anticipate, backInOut, circInOut } from 'motion-utils'; + +const unsupportedEasingFunctions = { + anticipate, + backInOut, + circInOut, +}; +function isUnsupportedEase(key) { + return key in unsupportedEasingFunctions; +} +function replaceStringEasing(transition) { + if (typeof transition.ease === "string" && + isUnsupportedEase(transition.ease)) { + transition.ease = unsupportedEasingFunctions[transition.ease]; + } +} + +export { replaceStringEasing }; diff --git a/node_modules/motion-dom/dist/es/effects/MotionValueState.mjs b/node_modules/motion-dom/dist/es/effects/MotionValueState.mjs new file mode 100644 index 00000000..ee0247d3 --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/MotionValueState.mjs @@ -0,0 +1,47 @@ +import { frame, cancelFrame } from '../frameloop/frame.mjs'; +import { numberValueTypes } from '../value/types/maps/number.mjs'; +import { getValueAsType } from '../value/types/utils/get-as-type.mjs'; + +class MotionValueState { + constructor() { + this.latest = {}; + this.values = new Map(); + } + set(name, value, render, computed, useDefaultValueType = true) { + const existingValue = this.values.get(name); + if (existingValue) { + existingValue.onRemove(); + } + const onChange = () => { + const v = value.get(); + if (useDefaultValueType) { + this.latest[name] = getValueAsType(v, numberValueTypes[name]); + } + else { + this.latest[name] = v; + } + render && frame.render(render); + }; + onChange(); + const cancelOnChange = value.on("change", onChange); + computed && value.addDependent(computed); + const remove = () => { + cancelOnChange(); + render && cancelFrame(render); + this.values.delete(name); + computed && value.removeDependent(computed); + }; + this.values.set(name, { value, onRemove: remove }); + return remove; + } + get(name) { + return this.values.get(name)?.value; + } + destroy() { + for (const value of this.values.values()) { + value.onRemove(); + } + } +} + +export { MotionValueState }; diff --git a/node_modules/motion-dom/dist/es/effects/attr/index.mjs b/node_modules/motion-dom/dist/es/effects/attr/index.mjs new file mode 100644 index 00000000..983a60ba --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/attr/index.mjs @@ -0,0 +1,41 @@ +import { camelToDash } from '../../render/dom/utils/camel-to-dash.mjs'; +import { createSelectorEffect } from '../utils/create-dom-effect.mjs'; +import { createEffect } from '../utils/create-effect.mjs'; + +function canSetAsProperty(element, name) { + if (!(name in element)) + return false; + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), name) || + Object.getOwnPropertyDescriptor(element, name); + // Check if it has a setter + return descriptor && typeof descriptor.set === "function"; +} +const addAttrValue = (element, state, key, value) => { + const isProp = canSetAsProperty(element, key); + const name = isProp + ? key + : key.startsWith("data") || key.startsWith("aria") + ? camelToDash(key) + : key; + /** + * Set attribute directly via property if available + */ + const render = isProp + ? () => { + element[name] = state.latest[key]; + } + : () => { + const v = state.latest[key]; + if (v === null || v === undefined) { + element.removeAttribute(name); + } + else { + element.setAttribute(name, String(v)); + } + }; + return state.set(key, value, render); +}; +const attrEffect = /*@__PURE__*/ createSelectorEffect( +/*@__PURE__*/ createEffect(addAttrValue)); + +export { addAttrValue, attrEffect }; diff --git a/node_modules/motion-dom/dist/es/effects/prop/index.mjs b/node_modules/motion-dom/dist/es/effects/prop/index.mjs new file mode 100644 index 00000000..46dfa9f8 --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/prop/index.mjs @@ -0,0 +1,9 @@ +import { createEffect } from '../utils/create-effect.mjs'; + +const propEffect = /*@__PURE__*/ createEffect((subject, state, key, value) => { + return state.set(key, value, () => { + subject[key] = state.latest[key]; + }, undefined, false); +}); + +export { propEffect }; diff --git a/node_modules/motion-dom/dist/es/effects/style/index.mjs b/node_modules/motion-dom/dist/es/effects/style/index.mjs new file mode 100644 index 00000000..13a86f48 --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/style/index.mjs @@ -0,0 +1,52 @@ +import { isCSSVar } from '../../render/dom/is-css-var.mjs'; +import { transformProps } from '../../render/utils/keys-transform.mjs'; +import { isHTMLElement } from '../../utils/is-html-element.mjs'; +import { MotionValue } from '../../value/index.mjs'; +import { createSelectorEffect } from '../utils/create-dom-effect.mjs'; +import { createEffect } from '../utils/create-effect.mjs'; +import { buildTransform } from './transform.mjs'; + +const originProps = new Set(["originX", "originY", "originZ"]); +const addStyleValue = (element, state, key, value) => { + let render = undefined; + let computed = undefined; + if (transformProps.has(key)) { + if (!state.get("transform")) { + // If this is an HTML element, we need to set the transform-box to fill-box + // to normalise the transform relative to the element's bounding box + if (!isHTMLElement(element) && !state.get("transformBox")) { + addStyleValue(element, state, "transformBox", new MotionValue("fill-box")); + } + state.set("transform", new MotionValue("none"), () => { + element.style.transform = buildTransform(state); + }); + } + computed = state.get("transform"); + } + else if (originProps.has(key)) { + if (!state.get("transformOrigin")) { + state.set("transformOrigin", new MotionValue(""), () => { + const originX = state.latest.originX ?? "50%"; + const originY = state.latest.originY ?? "50%"; + const originZ = state.latest.originZ ?? 0; + element.style.transformOrigin = `${originX} ${originY} ${originZ}`; + }); + } + computed = state.get("transformOrigin"); + } + else if (isCSSVar(key)) { + render = () => { + element.style.setProperty(key, state.latest[key]); + }; + } + else { + render = () => { + element.style[key] = state.latest[key]; + }; + } + return state.set(key, value, render, computed); +}; +const styleEffect = /*@__PURE__*/ createSelectorEffect( +/*@__PURE__*/ createEffect(addStyleValue)); + +export { addStyleValue, styleEffect }; diff --git a/node_modules/motion-dom/dist/es/effects/style/transform.mjs b/node_modules/motion-dom/dist/es/effects/style/transform.mjs new file mode 100644 index 00000000..63077c64 --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/style/transform.mjs @@ -0,0 +1,38 @@ +import { transformPropOrder } from '../../render/utils/keys-transform.mjs'; + +const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", +}; +function buildTransform(state) { + let transform = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < transformPropOrder.length; i++) { + const key = transformPropOrder[i]; + const value = state.latest[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + const valueToRender = state.latest[key]; + transform += `${transformName}(${valueToRender}) `; + } + } + return transformIsDefault ? "none" : transform.trim(); +} + +export { buildTransform }; diff --git a/node_modules/motion-dom/dist/es/effects/svg/index.mjs b/node_modules/motion-dom/dist/es/effects/svg/index.mjs new file mode 100644 index 00000000..42bba9fa --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/svg/index.mjs @@ -0,0 +1,41 @@ +import { MotionValue } from '../../value/index.mjs'; +import { px } from '../../value/types/numbers/units.mjs'; +import { addAttrValue } from '../attr/index.mjs'; +import { addStyleValue } from '../style/index.mjs'; +import { createSelectorEffect } from '../utils/create-dom-effect.mjs'; +import { createEffect } from '../utils/create-effect.mjs'; +import { frame } from '../../frameloop/frame.mjs'; + +const toPx = px.transform; +function addSVGPathValue(element, state, key, value) { + frame.render(() => element.setAttribute("pathLength", "1")); + if (key === "pathOffset") { + return state.set(key, value, () => element.setAttribute("stroke-dashoffset", toPx(-state.latest[key]))); + } + else { + if (!state.get("stroke-dasharray")) { + state.set("stroke-dasharray", new MotionValue("1 1"), () => { + const { pathLength = 1, pathSpacing } = state.latest; + element.setAttribute("stroke-dasharray", `${toPx(pathLength)} ${toPx(pathSpacing ?? 1 - Number(pathLength))}`); + }); + } + return state.set(key, value, undefined, state.get("stroke-dasharray")); + } +} +const addSVGValue = (element, state, key, value) => { + if (key.startsWith("path")) { + return addSVGPathValue(element, state, key, value); + } + else if (key.startsWith("attr")) { + return addAttrValue(element, state, convertAttrKey(key), value); + } + const handler = key in element.style ? addStyleValue : addAttrValue; + return handler(element, state, key, value); +}; +const svgEffect = /*@__PURE__*/ createSelectorEffect( +/*@__PURE__*/ createEffect(addSVGValue)); +function convertAttrKey(key) { + return key.replace(/^attr([A-Z])/, (_, firstChar) => firstChar.toLowerCase()); +} + +export { svgEffect }; diff --git a/node_modules/motion-dom/dist/es/effects/utils/create-dom-effect.mjs b/node_modules/motion-dom/dist/es/effects/utils/create-dom-effect.mjs new file mode 100644 index 00000000..1bc129e9 --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/utils/create-dom-effect.mjs @@ -0,0 +1,18 @@ +import { resolveElements } from '../../utils/resolve-elements.mjs'; + +function createSelectorEffect(subjectEffect) { + return (subject, values) => { + const elements = resolveElements(subject); + const subscriptions = []; + for (const element of elements) { + const remove = subjectEffect(element, values); + subscriptions.push(remove); + } + return () => { + for (const remove of subscriptions) + remove(); + }; + }; +} + +export { createSelectorEffect }; diff --git a/node_modules/motion-dom/dist/es/effects/utils/create-effect.mjs b/node_modules/motion-dom/dist/es/effects/utils/create-effect.mjs new file mode 100644 index 00000000..898d4b4d --- /dev/null +++ b/node_modules/motion-dom/dist/es/effects/utils/create-effect.mjs @@ -0,0 +1,21 @@ +import { MotionValueState } from '../MotionValueState.mjs'; + +function createEffect(addValue) { + const stateCache = new WeakMap(); + const subscriptions = []; + return (subject, values) => { + const state = stateCache.get(subject) ?? new MotionValueState(); + stateCache.set(subject, state); + for (const key in values) { + const value = values[key]; + const remove = addValue(subject, state, key, value); + subscriptions.push(remove); + } + return () => { + for (const cancel of subscriptions) + cancel(); + }; + }; +} + +export { createEffect }; diff --git a/node_modules/motion-dom/dist/es/frameloop/batcher.mjs b/node_modules/motion-dom/dist/es/frameloop/batcher.mjs new file mode 100644 index 00000000..44d20055 --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/batcher.mjs @@ -0,0 +1,71 @@ +import { MotionGlobalConfig } from 'motion-utils'; +import { stepsOrder } from './order.mjs'; +import { createRenderStep } from './render-step.mjs'; + +const maxElapsed = 40; +function createRenderBatcher(scheduleNextBatch, allowKeepAlive) { + let runNextFrame = false; + let useDefaultElapsed = true; + const state = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + const flagRunNextFrame = () => (runNextFrame = true); + const steps = stepsOrder.reduce((acc, key) => { + acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined); + return acc; + }, {}); + const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps; + const processBatch = () => { + const timestamp = MotionGlobalConfig.useManualTiming + ? state.timestamp + : performance.now(); + runNextFrame = false; + if (!MotionGlobalConfig.useManualTiming) { + state.delta = useDefaultElapsed + ? 1000 / 60 + : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1); + } + state.timestamp = timestamp; + state.isProcessing = true; + // Unrolled render loop for better per-frame performance + setup.process(state); + read.process(state); + resolveKeyframes.process(state); + preUpdate.process(state); + update.process(state); + preRender.process(state); + render.process(state); + postRender.process(state); + state.isProcessing = false; + if (runNextFrame && allowKeepAlive) { + useDefaultElapsed = false; + scheduleNextBatch(processBatch); + } + }; + const wake = () => { + runNextFrame = true; + useDefaultElapsed = true; + if (!state.isProcessing) { + scheduleNextBatch(processBatch); + } + }; + const schedule = stepsOrder.reduce((acc, key) => { + const step = steps[key]; + acc[key] = (process, keepAlive = false, immediate = false) => { + if (!runNextFrame) + wake(); + return step.schedule(process, keepAlive, immediate); + }; + return acc; + }, {}); + const cancel = (process) => { + for (let i = 0; i < stepsOrder.length; i++) { + steps[stepsOrder[i]].cancel(process); + } + }; + return { schedule, cancel, state, steps }; +} + +export { createRenderBatcher }; diff --git a/node_modules/motion-dom/dist/es/frameloop/frame.mjs b/node_modules/motion-dom/dist/es/frameloop/frame.mjs new file mode 100644 index 00000000..98f225ba --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/frame.mjs @@ -0,0 +1,6 @@ +import { noop } from 'motion-utils'; +import { createRenderBatcher } from './batcher.mjs'; + +const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true); + +export { cancelFrame, frame, frameData, frameSteps }; diff --git a/node_modules/motion-dom/dist/es/frameloop/index-legacy.mjs b/node_modules/motion-dom/dist/es/frameloop/index-legacy.mjs new file mode 100644 index 00000000..8edf61be --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/index-legacy.mjs @@ -0,0 +1,20 @@ +import { stepsOrder } from './order.mjs'; +import { frame, cancelFrame } from './frame.mjs'; + +/** + * @deprecated + * + * Import as `frame` instead. + */ +const sync = frame; +/** + * @deprecated + * + * Use cancelFrame(callback) instead. + */ +const cancelSync = stepsOrder.reduce((acc, key) => { + acc[key] = (process) => cancelFrame(process); + return acc; +}, {}); + +export { cancelSync, sync }; diff --git a/node_modules/motion-dom/dist/es/frameloop/microtask.mjs b/node_modules/motion-dom/dist/es/frameloop/microtask.mjs new file mode 100644 index 00000000..f0570afb --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/microtask.mjs @@ -0,0 +1,6 @@ +import { createRenderBatcher } from './batcher.mjs'; + +const { schedule: microtask, cancel: cancelMicrotask } = +/* @__PURE__ */ createRenderBatcher(queueMicrotask, false); + +export { cancelMicrotask, microtask }; diff --git a/node_modules/motion-dom/dist/es/frameloop/order.mjs b/node_modules/motion-dom/dist/es/frameloop/order.mjs new file mode 100644 index 00000000..1a3706fa --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/order.mjs @@ -0,0 +1,12 @@ +const stepsOrder = [ + "setup", // Compute + "read", // Read + "resolveKeyframes", // Write/Read/Write/Read + "preUpdate", // Compute + "update", // Compute + "preRender", // Compute + "render", // Write + "postRender", // Compute +]; + +export { stepsOrder }; diff --git a/node_modules/motion-dom/dist/es/frameloop/render-step.mjs b/node_modules/motion-dom/dist/es/frameloop/render-step.mjs new file mode 100644 index 00000000..18f59b6f --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/render-step.mjs @@ -0,0 +1,92 @@ +import { statsBuffer } from '../stats/buffer.mjs'; + +function createRenderStep(runNextFrame, stepName) { + /** + * We create and reuse two queues, one to queue jobs for the current frame + * and one for the next. We reuse to avoid triggering GC after x frames. + */ + let thisFrame = new Set(); + let nextFrame = new Set(); + /** + * Track whether we're currently processing jobs in this step. This way + * we can decide whether to schedule new jobs for this frame or next. + */ + let isProcessing = false; + let flushNextFrame = false; + /** + * A set of processes which were marked keepAlive when scheduled. + */ + const toKeepAlive = new WeakSet(); + let latestFrameData = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + let numCalls = 0; + function triggerCallback(callback) { + if (toKeepAlive.has(callback)) { + step.schedule(callback); + runNextFrame(); + } + numCalls++; + callback(latestFrameData); + } + const step = { + /** + * Schedule a process to run on the next frame. + */ + schedule: (callback, keepAlive = false, immediate = false) => { + const addToCurrentFrame = immediate && isProcessing; + const queue = addToCurrentFrame ? thisFrame : nextFrame; + if (keepAlive) + toKeepAlive.add(callback); + if (!queue.has(callback)) + queue.add(callback); + return callback; + }, + /** + * Cancel the provided callback from running on the next frame. + */ + cancel: (callback) => { + nextFrame.delete(callback); + toKeepAlive.delete(callback); + }, + /** + * Execute all schedule callbacks. + */ + process: (frameData) => { + latestFrameData = frameData; + /** + * If we're already processing we've probably been triggered by a flushSync + * inside an existing process. Instead of executing, mark flushNextFrame + * as true and ensure we flush the following frame at the end of this one. + */ + if (isProcessing) { + flushNextFrame = true; + return; + } + isProcessing = true; + [thisFrame, nextFrame] = [nextFrame, thisFrame]; + // Execute this frame + thisFrame.forEach(triggerCallback); + /** + * If we're recording stats then + */ + if (stepName && statsBuffer.value) { + statsBuffer.value.frameloop[stepName].push(numCalls); + } + numCalls = 0; + // Clear the frame so no callbacks remain. This is to avoid + // memory leaks should this render step not run for a while. + thisFrame.clear(); + isProcessing = false; + if (flushNextFrame) { + flushNextFrame = false; + step.process(frameData); + } + }, + }; + return step; +} + +export { createRenderStep }; diff --git a/node_modules/motion-dom/dist/es/frameloop/sync-time.mjs b/node_modules/motion-dom/dist/es/frameloop/sync-time.mjs new file mode 100644 index 00000000..ec9bcb85 --- /dev/null +++ b/node_modules/motion-dom/dist/es/frameloop/sync-time.mjs @@ -0,0 +1,31 @@ +import { MotionGlobalConfig } from 'motion-utils'; +import { frameData } from './frame.mjs'; + +let now; +function clearTime() { + now = undefined; +} +/** + * An eventloop-synchronous alternative to performance.now(). + * + * Ensures that time measurements remain consistent within a synchronous context. + * Usually calling performance.now() twice within the same synchronous context + * will return different values which isn't useful for animations when we're usually + * trying to sync animations to the same frame. + */ +const time = { + now: () => { + if (now === undefined) { + time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming + ? frameData.timestamp + : performance.now()); + } + return now; + }, + set: (newTime) => { + now = newTime; + queueMicrotask(clearTime); + }, +}; + +export { time }; diff --git a/node_modules/motion-dom/dist/es/gestures/drag/state/is-active.mjs b/node_modules/motion-dom/dist/es/gestures/drag/state/is-active.mjs new file mode 100644 index 00000000..731e83fd --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/drag/state/is-active.mjs @@ -0,0 +1,9 @@ +const isDragging = { + x: false, + y: false, +}; +function isDragActive() { + return isDragging.x || isDragging.y; +} + +export { isDragActive, isDragging }; diff --git a/node_modules/motion-dom/dist/es/gestures/drag/state/set-active.mjs b/node_modules/motion-dom/dist/es/gestures/drag/state/set-active.mjs new file mode 100644 index 00000000..f560fe57 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/drag/state/set-active.mjs @@ -0,0 +1,28 @@ +import { isDragging } from './is-active.mjs'; + +function setDragLock(axis) { + if (axis === "x" || axis === "y") { + if (isDragging[axis]) { + return null; + } + else { + isDragging[axis] = true; + return () => { + isDragging[axis] = false; + }; + } + } + else { + if (isDragging.x || isDragging.y) { + return null; + } + else { + isDragging.x = isDragging.y = true; + return () => { + isDragging.x = isDragging.y = false; + }; + } + } +} + +export { setDragLock }; diff --git a/node_modules/motion-dom/dist/es/gestures/hover.mjs b/node_modules/motion-dom/dist/es/gestures/hover.mjs new file mode 100644 index 00000000..ec58b203 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/hover.mjs @@ -0,0 +1,37 @@ +import { isDragActive } from './drag/state/is-active.mjs'; +import { setupGesture } from './utils/setup.mjs'; + +function isValidHover(event) { + return !(event.pointerType === "touch" || isDragActive()); +} +/** + * Create a hover gesture. hover() is different to .addEventListener("pointerenter") + * in that it has an easier syntax, filters out polyfilled touch events, interoperates + * with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends. + * + * @public + */ +function hover(elementOrSelector, onHoverStart, options = {}) { + const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options); + const onPointerEnter = (enterEvent) => { + if (!isValidHover(enterEvent)) + return; + const { target } = enterEvent; + const onHoverEnd = onHoverStart(target, enterEvent); + if (typeof onHoverEnd !== "function" || !target) + return; + const onPointerLeave = (leaveEvent) => { + if (!isValidHover(leaveEvent)) + return; + onHoverEnd(leaveEvent); + target.removeEventListener("pointerleave", onPointerLeave); + }; + target.addEventListener("pointerleave", onPointerLeave, eventOptions); + }; + elements.forEach((element) => { + element.addEventListener("pointerenter", onPointerEnter, eventOptions); + }); + return cancel; +} + +export { hover }; diff --git a/node_modules/motion-dom/dist/es/gestures/press/index.mjs b/node_modules/motion-dom/dist/es/gestures/press/index.mjs new file mode 100644 index 00000000..11f967b2 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/press/index.mjs @@ -0,0 +1,83 @@ +import { isHTMLElement } from '../../utils/is-html-element.mjs'; +import { isDragActive } from '../drag/state/is-active.mjs'; +import { isNodeOrChild } from '../utils/is-node-or-child.mjs'; +import { isPrimaryPointer } from '../utils/is-primary-pointer.mjs'; +import { setupGesture } from '../utils/setup.mjs'; +import { isElementKeyboardAccessible } from './utils/is-keyboard-accessible.mjs'; +import { enableKeyboardPress } from './utils/keyboard.mjs'; +import { isPressing } from './utils/state.mjs'; + +/** + * Filter out events that are not primary pointer events, or are triggering + * while a Motion gesture is active. + */ +function isValidPressEvent(event) { + return isPrimaryPointer(event) && !isDragActive(); +} +/** + * Create a press gesture. + * + * Press is different to `"pointerdown"`, `"pointerup"` in that it + * automatically filters out secondary pointer events like right + * click and multitouch. + * + * It also adds accessibility support for keyboards, where + * an element with a press gesture will receive focus and + * trigger on Enter `"keydown"` and `"keyup"` events. + * + * This is different to a browser's `"click"` event, which does + * respond to keyboards but only for the `"click"` itself, rather + * than the press start and end/cancel. The element also needs + * to be focusable for this to work, whereas a press gesture will + * make an element focusable by default. + * + * @public + */ +function press(targetOrSelector, onPressStart, options = {}) { + const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options); + const startPress = (startEvent) => { + const target = startEvent.currentTarget; + if (!isValidPressEvent(startEvent)) + return; + isPressing.add(target); + const onPressEnd = onPressStart(target, startEvent); + const onPointerEnd = (endEvent, success) => { + window.removeEventListener("pointerup", onPointerUp); + window.removeEventListener("pointercancel", onPointerCancel); + if (isPressing.has(target)) { + isPressing.delete(target); + } + if (!isValidPressEvent(endEvent)) { + return; + } + if (typeof onPressEnd === "function") { + onPressEnd(endEvent, { success }); + } + }; + const onPointerUp = (upEvent) => { + onPointerEnd(upEvent, target === window || + target === document || + options.useGlobalTarget || + isNodeOrChild(target, upEvent.target)); + }; + const onPointerCancel = (cancelEvent) => { + onPointerEnd(cancelEvent, false); + }; + window.addEventListener("pointerup", onPointerUp, eventOptions); + window.addEventListener("pointercancel", onPointerCancel, eventOptions); + }; + targets.forEach((target) => { + const pointerDownTarget = options.useGlobalTarget ? window : target; + pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions); + if (isHTMLElement(target)) { + target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions)); + if (!isElementKeyboardAccessible(target) && + !target.hasAttribute("tabindex")) { + target.tabIndex = 0; + } + } + }); + return cancelEvents; +} + +export { press }; diff --git a/node_modules/motion-dom/dist/es/gestures/press/utils/is-keyboard-accessible.mjs b/node_modules/motion-dom/dist/es/gestures/press/utils/is-keyboard-accessible.mjs new file mode 100644 index 00000000..67029883 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/press/utils/is-keyboard-accessible.mjs @@ -0,0 +1,13 @@ +const focusableElements = new Set([ + "BUTTON", + "INPUT", + "SELECT", + "TEXTAREA", + "A", +]); +function isElementKeyboardAccessible(element) { + return (focusableElements.has(element.tagName) || + element.tabIndex !== -1); +} + +export { isElementKeyboardAccessible }; diff --git a/node_modules/motion-dom/dist/es/gestures/press/utils/keyboard.mjs b/node_modules/motion-dom/dist/es/gestures/press/utils/keyboard.mjs new file mode 100644 index 00000000..80e4e5ee --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/press/utils/keyboard.mjs @@ -0,0 +1,38 @@ +import { isPressing } from './state.mjs'; + +/** + * Filter out events that are not "Enter" keys. + */ +function filterEvents(callback) { + return (event) => { + if (event.key !== "Enter") + return; + callback(event); + }; +} +function firePointerEvent(target, type) { + target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true })); +} +const enableKeyboardPress = (focusEvent, eventOptions) => { + const element = focusEvent.currentTarget; + if (!element) + return; + const handleKeydown = filterEvents(() => { + if (isPressing.has(element)) + return; + firePointerEvent(element, "down"); + const handleKeyup = filterEvents(() => { + firePointerEvent(element, "up"); + }); + const handleBlur = () => firePointerEvent(element, "cancel"); + element.addEventListener("keyup", handleKeyup, eventOptions); + element.addEventListener("blur", handleBlur, eventOptions); + }); + element.addEventListener("keydown", handleKeydown, eventOptions); + /** + * Add an event listener that fires on blur to remove the keydown events. + */ + element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions); +}; + +export { enableKeyboardPress }; diff --git a/node_modules/motion-dom/dist/es/gestures/press/utils/state.mjs b/node_modules/motion-dom/dist/es/gestures/press/utils/state.mjs new file mode 100644 index 00000000..15983982 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/press/utils/state.mjs @@ -0,0 +1,3 @@ +const isPressing = new WeakSet(); + +export { isPressing }; diff --git a/node_modules/motion-dom/dist/es/gestures/utils/is-node-or-child.mjs b/node_modules/motion-dom/dist/es/gestures/utils/is-node-or-child.mjs new file mode 100644 index 00000000..fac227f6 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/utils/is-node-or-child.mjs @@ -0,0 +1,20 @@ +/** + * Recursively traverse up the tree to check whether the provided child node + * is the parent or a descendant of it. + * + * @param parent - Element to find + * @param child - Element to test against parent + */ +const isNodeOrChild = (parent, child) => { + if (!child) { + return false; + } + else if (parent === child) { + return true; + } + else { + return isNodeOrChild(parent, child.parentElement); + } +}; + +export { isNodeOrChild }; diff --git a/node_modules/motion-dom/dist/es/gestures/utils/is-primary-pointer.mjs b/node_modules/motion-dom/dist/es/gestures/utils/is-primary-pointer.mjs new file mode 100644 index 00000000..02665fbc --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/utils/is-primary-pointer.mjs @@ -0,0 +1,18 @@ +const isPrimaryPointer = (event) => { + if (event.pointerType === "mouse") { + return typeof event.button !== "number" || event.button <= 0; + } + else { + /** + * isPrimary is true for all mice buttons, whereas every touch point + * is regarded as its own input. So subsequent concurrent touch points + * will be false. + * + * Specifically match against false here as incomplete versions of + * PointerEvents in very old browser might have it set as undefined. + */ + return event.isPrimary !== false; + } +}; + +export { isPrimaryPointer }; diff --git a/node_modules/motion-dom/dist/es/gestures/utils/setup.mjs b/node_modules/motion-dom/dist/es/gestures/utils/setup.mjs new file mode 100644 index 00000000..8932c252 --- /dev/null +++ b/node_modules/motion-dom/dist/es/gestures/utils/setup.mjs @@ -0,0 +1,15 @@ +import { resolveElements } from '../../utils/resolve-elements.mjs'; + +function setupGesture(elementOrSelector, options) { + const elements = resolveElements(elementOrSelector); + const gestureAbortController = new AbortController(); + const eventOptions = { + passive: true, + ...options, + signal: gestureAbortController.signal, + }; + const cancel = () => gestureAbortController.abort(); + return [elements, eventOptions, cancel]; +} + +export { setupGesture }; diff --git a/node_modules/motion-dom/dist/es/index.mjs b/node_modules/motion-dom/dist/es/index.mjs new file mode 100644 index 00000000..519ef892 --- /dev/null +++ b/node_modules/motion-dom/dist/es/index.mjs @@ -0,0 +1,96 @@ +export { AsyncMotionValueAnimation } from './animation/AsyncMotionValueAnimation.mjs'; +export { GroupAnimation } from './animation/GroupAnimation.mjs'; +export { GroupAnimationWithThen } from './animation/GroupAnimationWithThen.mjs'; +export { JSAnimation, animateValue } from './animation/JSAnimation.mjs'; +export { NativeAnimation } from './animation/NativeAnimation.mjs'; +export { NativeAnimationExtended } from './animation/NativeAnimationExtended.mjs'; +export { NativeAnimationWrapper } from './animation/NativeAnimationWrapper.mjs'; +export { animationMapKey, getAnimationMap } from './animation/utils/active-animations.mjs'; +export { getVariableValue, parseCSSVariable } from './animation/utils/css-variables-conversion.mjs'; +export { getValueTransition } from './animation/utils/get-value-transition.mjs'; +export { isCSSVariableName, isCSSVariableToken } from './animation/utils/is-css-variable.mjs'; +export { inertia } from './animation/generators/inertia.mjs'; +export { defaultEasing, keyframes } from './animation/generators/keyframes.mjs'; +export { spring } from './animation/generators/spring/index.mjs'; +export { calcGeneratorDuration, maxGeneratorDuration } from './animation/generators/utils/calc-duration.mjs'; +export { createGeneratorEasing } from './animation/generators/utils/create-generator-easing.mjs'; +export { isGenerator } from './animation/generators/utils/is-generator.mjs'; +export { DOMKeyframesResolver } from './animation/keyframes/DOMKeyframesResolver.mjs'; +export { KeyframeResolver, flushKeyframeResolvers } from './animation/keyframes/KeyframesResolver.mjs'; +export { defaultOffset } from './animation/keyframes/offsets/default.mjs'; +export { fillOffset } from './animation/keyframes/offsets/fill.mjs'; +export { convertOffsetToTimes } from './animation/keyframes/offsets/time.mjs'; +export { applyPxDefaults } from './animation/keyframes/utils/apply-px-defaults.mjs'; +export { fillWildcards } from './animation/keyframes/utils/fill-wildcards.mjs'; +export { cubicBezierAsString } from './animation/waapi/easing/cubic-bezier.mjs'; +export { isWaapiSupportedEasing } from './animation/waapi/easing/is-supported.mjs'; +export { mapEasingToNativeEasing } from './animation/waapi/easing/map-easing.mjs'; +export { supportedWaapiEasing } from './animation/waapi/easing/supported.mjs'; +export { startWaapiAnimation } from './animation/waapi/start-waapi-animation.mjs'; +export { supportsPartialKeyframes } from './animation/waapi/supports/partial-keyframes.mjs'; +export { supportsBrowserAnimation } from './animation/waapi/supports/waapi.mjs'; +export { acceleratedValues } from './animation/waapi/utils/accelerated-values.mjs'; +export { generateLinearEasing } from './animation/waapi/utils/linear.mjs'; +export { addAttrValue, attrEffect } from './effects/attr/index.mjs'; +export { propEffect } from './effects/prop/index.mjs'; +export { addStyleValue, styleEffect } from './effects/style/index.mjs'; +export { svgEffect } from './effects/svg/index.mjs'; +export { createRenderBatcher } from './frameloop/batcher.mjs'; +export { cancelMicrotask, microtask } from './frameloop/microtask.mjs'; +export { time } from './frameloop/sync-time.mjs'; +export { isDragActive, isDragging } from './gestures/drag/state/is-active.mjs'; +export { setDragLock } from './gestures/drag/state/set-active.mjs'; +export { hover } from './gestures/hover.mjs'; +export { press } from './gestures/press/index.mjs'; +export { isNodeOrChild } from './gestures/utils/is-node-or-child.mjs'; +export { isPrimaryPointer } from './gestures/utils/is-primary-pointer.mjs'; +export { defaultTransformValue, parseValueFromTransform, readTransformValue } from './render/dom/parse-transform.mjs'; +export { getComputedStyle } from './render/dom/style-computed.mjs'; +export { setStyle } from './render/dom/style-set.mjs'; +export { positionalKeys } from './render/utils/keys-position.mjs'; +export { transformPropOrder, transformProps } from './render/utils/keys-transform.mjs'; +export { resize } from './resize/index.mjs'; +export { observeTimeline } from './scroll/observe.mjs'; +export { recordStats } from './stats/index.mjs'; +export { activeAnimations } from './stats/animation-count.mjs'; +export { statsBuffer } from './stats/buffer.mjs'; +export { interpolate } from './utils/interpolate.mjs'; +export { isHTMLElement } from './utils/is-html-element.mjs'; +export { isSVGElement } from './utils/is-svg-element.mjs'; +export { isSVGSVGElement } from './utils/is-svg-svg-element.mjs'; +export { mix } from './utils/mix/index.mjs'; +export { mixColor, mixLinearColor } from './utils/mix/color.mjs'; +export { getMixer, mixArray, mixComplex, mixObject } from './utils/mix/complex.mjs'; +export { mixImmediate } from './utils/mix/immediate.mjs'; +export { mixNumber } from './utils/mix/number.mjs'; +export { invisibleValues, mixVisibility } from './utils/mix/visibility.mjs'; +export { resolveElements } from './utils/resolve-elements.mjs'; +export { getOriginIndex, stagger } from './utils/stagger.mjs'; +export { supportsFlags } from './utils/supports/flags.mjs'; +export { supportsLinearEasing } from './utils/supports/linear-easing.mjs'; +export { supportsScrollTimeline } from './utils/supports/scroll-timeline.mjs'; +export { transform } from './utils/transform.mjs'; +export { MotionValue, collectMotionValues, motionValue } from './value/index.mjs'; +export { mapValue } from './value/map-value.mjs'; +export { attachSpring, springValue } from './value/spring-value.mjs'; +export { transformValue } from './value/transform-value.mjs'; +export { color } from './value/types/color/index.mjs'; +export { hex } from './value/types/color/hex.mjs'; +export { hsla } from './value/types/color/hsla.mjs'; +export { hslaToRgba } from './value/types/color/hsla-to-rgba.mjs'; +export { rgbUnit, rgba } from './value/types/color/rgba.mjs'; +export { analyseComplexValue, complex } from './value/types/complex/index.mjs'; +export { dimensionValueTypes, findDimensionValueType } from './value/types/dimensions.mjs'; +export { defaultValueTypes, getDefaultValueType } from './value/types/maps/defaults.mjs'; +export { numberValueTypes } from './value/types/maps/number.mjs'; +export { transformValueTypes } from './value/types/maps/transform.mjs'; +export { alpha, number, scale } from './value/types/numbers/index.mjs'; +export { degrees, percent, progressPercentage, px, vh, vw } from './value/types/numbers/units.mjs'; +export { testValueType } from './value/types/test.mjs'; +export { getAnimatableNone } from './value/types/utils/animatable-none.mjs'; +export { findValueType } from './value/types/utils/find.mjs'; +export { getValueAsType } from './value/types/utils/get-as-type.mjs'; +export { isMotionValue } from './value/utils/is-motion-value.mjs'; +export { ViewTransitionBuilder, animateView } from './view/index.mjs'; +export { cancelSync, sync } from './frameloop/index-legacy.mjs'; +export { cancelFrame, frame, frameData, frameSteps } from './frameloop/frame.mjs'; diff --git a/node_modules/motion-dom/dist/es/render/dom/is-css-var.mjs b/node_modules/motion-dom/dist/es/render/dom/is-css-var.mjs new file mode 100644 index 00000000..01c70b61 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/dom/is-css-var.mjs @@ -0,0 +1,3 @@ +const isCSSVar = (name) => name.startsWith("--"); + +export { isCSSVar }; diff --git a/node_modules/motion-dom/dist/es/render/dom/parse-transform.mjs b/node_modules/motion-dom/dist/es/render/dom/parse-transform.mjs new file mode 100644 index 00000000..eadaacf4 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/dom/parse-transform.mjs @@ -0,0 +1,83 @@ +const radToDeg = (rad) => (rad * 180) / Math.PI; +const rotate = (v) => { + const angle = radToDeg(Math.atan2(v[1], v[0])); + return rebaseAngle(angle); +}; +const matrix2dParsers = { + x: 4, + y: 5, + translateX: 4, + translateY: 5, + scaleX: 0, + scaleY: 3, + scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2, + rotate, + rotateZ: rotate, + skewX: (v) => radToDeg(Math.atan(v[1])), + skewY: (v) => radToDeg(Math.atan(v[2])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2, +}; +const rebaseAngle = (angle) => { + angle = angle % 360; + if (angle < 0) + angle += 360; + return angle; +}; +const rotateZ = rotate; +const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]); +const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]); +const matrix3dParsers = { + x: 12, + y: 13, + z: 14, + translateX: 12, + translateY: 13, + translateZ: 14, + scaleX, + scaleY, + scale: (v) => (scaleX(v) + scaleY(v)) / 2, + rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))), + rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))), + rotateZ, + rotate: rotateZ, + skewX: (v) => radToDeg(Math.atan(v[4])), + skewY: (v) => radToDeg(Math.atan(v[1])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2, +}; +function defaultTransformValue(name) { + return name.includes("scale") ? 1 : 0; +} +function parseValueFromTransform(transform, name) { + if (!transform || transform === "none") { + return defaultTransformValue(name); + } + const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u); + let parsers; + let match; + if (matrix3dMatch) { + parsers = matrix3dParsers; + match = matrix3dMatch; + } + else { + const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u); + parsers = matrix2dParsers; + match = matrix2dMatch; + } + if (!match) { + return defaultTransformValue(name); + } + const valueParser = parsers[name]; + const values = match[1].split(",").map(convertTransformToNumber); + return typeof valueParser === "function" + ? valueParser(values) + : values[valueParser]; +} +const readTransformValue = (instance, name) => { + const { transform = "none" } = getComputedStyle(instance); + return parseValueFromTransform(transform, name); +}; +function convertTransformToNumber(value) { + return parseFloat(value.trim()); +} + +export { defaultTransformValue, parseValueFromTransform, readTransformValue }; diff --git a/node_modules/motion-dom/dist/es/render/dom/style-computed.mjs b/node_modules/motion-dom/dist/es/render/dom/style-computed.mjs new file mode 100644 index 00000000..fa25e700 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/dom/style-computed.mjs @@ -0,0 +1,10 @@ +import { isCSSVar } from './is-css-var.mjs'; + +function getComputedStyle(element, name) { + const computedStyle = window.getComputedStyle(element); + return isCSSVar(name) + ? computedStyle.getPropertyValue(name) + : computedStyle[name]; +} + +export { getComputedStyle }; diff --git a/node_modules/motion-dom/dist/es/render/dom/style-set.mjs b/node_modules/motion-dom/dist/es/render/dom/style-set.mjs new file mode 100644 index 00000000..61b7a756 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/dom/style-set.mjs @@ -0,0 +1,9 @@ +import { isCSSVar } from './is-css-var.mjs'; + +function setStyle(element, name, value) { + isCSSVar(name) + ? element.style.setProperty(name, value) + : (element.style[name] = value); +} + +export { setStyle }; diff --git a/node_modules/motion-dom/dist/es/render/dom/utils/camel-to-dash.mjs b/node_modules/motion-dom/dist/es/render/dom/utils/camel-to-dash.mjs new file mode 100644 index 00000000..80ff8a78 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/dom/utils/camel-to-dash.mjs @@ -0,0 +1,5 @@ +function camelToDash(str) { + return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`); +} + +export { camelToDash }; diff --git a/node_modules/motion-dom/dist/es/render/utils/keys-position.mjs b/node_modules/motion-dom/dist/es/render/utils/keys-position.mjs new file mode 100644 index 00000000..fcb71228 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/utils/keys-position.mjs @@ -0,0 +1,13 @@ +import { transformPropOrder } from './keys-transform.mjs'; + +const positionalKeys = new Set([ + "width", + "height", + "top", + "left", + "right", + "bottom", + ...transformPropOrder, +]); + +export { positionalKeys }; diff --git a/node_modules/motion-dom/dist/es/render/utils/keys-transform.mjs b/node_modules/motion-dom/dist/es/render/utils/keys-transform.mjs new file mode 100644 index 00000000..4c646e78 --- /dev/null +++ b/node_modules/motion-dom/dist/es/render/utils/keys-transform.mjs @@ -0,0 +1,28 @@ +/** + * Generate a list of every possible transform key. + */ +const transformPropOrder = [ + "transformPerspective", + "x", + "y", + "z", + "translateX", + "translateY", + "translateZ", + "scale", + "scaleX", + "scaleY", + "rotate", + "rotateX", + "rotateY", + "rotateZ", + "skew", + "skewX", + "skewY", +]; +/** + * A quick lookup for transform props. + */ +const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))(); + +export { transformPropOrder, transformProps }; diff --git a/node_modules/motion-dom/dist/es/resize/handle-element.mjs b/node_modules/motion-dom/dist/es/resize/handle-element.mjs new file mode 100644 index 00000000..d4528aed --- /dev/null +++ b/node_modules/motion-dom/dist/es/resize/handle-element.mjs @@ -0,0 +1,63 @@ +import { isSVGElement } from '../utils/is-svg-element.mjs'; +import { resolveElements } from '../utils/resolve-elements.mjs'; + +const resizeHandlers = new WeakMap(); +let observer; +const getSize = (borderBoxAxis, svgAxis, htmlAxis) => (target, borderBoxSize) => { + if (borderBoxSize && borderBoxSize[0]) { + return borderBoxSize[0][(borderBoxAxis + "Size")]; + } + else if (isSVGElement(target) && "getBBox" in target) { + return target.getBBox()[svgAxis]; + } + else { + return target[htmlAxis]; + } +}; +const getWidth = /*@__PURE__*/ getSize("inline", "width", "offsetWidth"); +const getHeight = /*@__PURE__*/ getSize("block", "height", "offsetHeight"); +function notifyTarget({ target, borderBoxSize }) { + resizeHandlers.get(target)?.forEach((handler) => { + handler(target, { + get width() { + return getWidth(target, borderBoxSize); + }, + get height() { + return getHeight(target, borderBoxSize); + }, + }); + }); +} +function notifyAll(entries) { + entries.forEach(notifyTarget); +} +function createResizeObserver() { + if (typeof ResizeObserver === "undefined") + return; + observer = new ResizeObserver(notifyAll); +} +function resizeElement(target, handler) { + if (!observer) + createResizeObserver(); + const elements = resolveElements(target); + elements.forEach((element) => { + let elementHandlers = resizeHandlers.get(element); + if (!elementHandlers) { + elementHandlers = new Set(); + resizeHandlers.set(element, elementHandlers); + } + elementHandlers.add(handler); + observer?.observe(element); + }); + return () => { + elements.forEach((element) => { + const elementHandlers = resizeHandlers.get(element); + elementHandlers?.delete(handler); + if (!elementHandlers?.size) { + observer?.unobserve(element); + } + }); + }; +} + +export { resizeElement }; diff --git a/node_modules/motion-dom/dist/es/resize/handle-window.mjs b/node_modules/motion-dom/dist/es/resize/handle-window.mjs new file mode 100644 index 00000000..fac3224f --- /dev/null +++ b/node_modules/motion-dom/dist/es/resize/handle-window.mjs @@ -0,0 +1,31 @@ +const windowCallbacks = new Set(); +let windowResizeHandler; +function createWindowResizeHandler() { + windowResizeHandler = () => { + const info = { + get width() { + return window.innerWidth; + }, + get height() { + return window.innerHeight; + }, + }; + windowCallbacks.forEach((callback) => callback(info)); + }; + window.addEventListener("resize", windowResizeHandler); +} +function resizeWindow(callback) { + windowCallbacks.add(callback); + if (!windowResizeHandler) + createWindowResizeHandler(); + return () => { + windowCallbacks.delete(callback); + if (!windowCallbacks.size && + typeof windowResizeHandler === "function") { + window.removeEventListener("resize", windowResizeHandler); + windowResizeHandler = undefined; + } + }; +} + +export { resizeWindow }; diff --git a/node_modules/motion-dom/dist/es/resize/index.mjs b/node_modules/motion-dom/dist/es/resize/index.mjs new file mode 100644 index 00000000..d372fb11 --- /dev/null +++ b/node_modules/motion-dom/dist/es/resize/index.mjs @@ -0,0 +1,8 @@ +import { resizeElement } from './handle-element.mjs'; +import { resizeWindow } from './handle-window.mjs'; + +function resize(a, b) { + return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b); +} + +export { resize }; diff --git a/node_modules/motion-dom/dist/es/scroll/observe.mjs b/node_modules/motion-dom/dist/es/scroll/observe.mjs new file mode 100644 index 00000000..b2895d78 --- /dev/null +++ b/node_modules/motion-dom/dist/es/scroll/observe.mjs @@ -0,0 +1,18 @@ +import { frame, cancelFrame } from '../frameloop/frame.mjs'; + +function observeTimeline(update, timeline) { + let prevProgress; + const onFrame = () => { + const { currentTime } = timeline; + const percentage = currentTime === null ? 0 : currentTime.value; + const progress = percentage / 100; + if (prevProgress !== progress) { + update(progress); + } + prevProgress = progress; + }; + frame.preUpdate(onFrame, true); + return () => cancelFrame(onFrame); +} + +export { observeTimeline }; diff --git a/node_modules/motion-dom/dist/es/stats/animation-count.mjs b/node_modules/motion-dom/dist/es/stats/animation-count.mjs new file mode 100644 index 00000000..cfd503c4 --- /dev/null +++ b/node_modules/motion-dom/dist/es/stats/animation-count.mjs @@ -0,0 +1,7 @@ +const activeAnimations = { + layout: 0, + mainThread: 0, + waapi: 0, +}; + +export { activeAnimations }; diff --git a/node_modules/motion-dom/dist/es/stats/buffer.mjs b/node_modules/motion-dom/dist/es/stats/buffer.mjs new file mode 100644 index 00000000..de2a1744 --- /dev/null +++ b/node_modules/motion-dom/dist/es/stats/buffer.mjs @@ -0,0 +1,6 @@ +const statsBuffer = { + value: null, + addProjectionMetrics: null, +}; + +export { statsBuffer }; diff --git a/node_modules/motion-dom/dist/es/stats/index.mjs b/node_modules/motion-dom/dist/es/stats/index.mjs new file mode 100644 index 00000000..8f9f6201 --- /dev/null +++ b/node_modules/motion-dom/dist/es/stats/index.mjs @@ -0,0 +1,117 @@ +import { activeAnimations } from './animation-count.mjs'; +import { statsBuffer } from './buffer.mjs'; +import { frame, cancelFrame, frameData } from '../frameloop/frame.mjs'; + +function record() { + const { value } = statsBuffer; + if (value === null) { + cancelFrame(record); + return; + } + value.frameloop.rate.push(frameData.delta); + value.animations.mainThread.push(activeAnimations.mainThread); + value.animations.waapi.push(activeAnimations.waapi); + value.animations.layout.push(activeAnimations.layout); +} +function mean(values) { + return values.reduce((acc, value) => acc + value, 0) / values.length; +} +function summarise(values, calcAverage = mean) { + if (values.length === 0) { + return { + min: 0, + max: 0, + avg: 0, + }; + } + return { + min: Math.min(...values), + max: Math.max(...values), + avg: calcAverage(values), + }; +} +const msToFps = (ms) => Math.round(1000 / ms); +function clearStatsBuffer() { + statsBuffer.value = null; + statsBuffer.addProjectionMetrics = null; +} +function reportStats() { + const { value } = statsBuffer; + if (!value) { + throw new Error("Stats are not being measured"); + } + clearStatsBuffer(); + cancelFrame(record); + const summary = { + frameloop: { + setup: summarise(value.frameloop.setup), + rate: summarise(value.frameloop.rate), + read: summarise(value.frameloop.read), + resolveKeyframes: summarise(value.frameloop.resolveKeyframes), + preUpdate: summarise(value.frameloop.preUpdate), + update: summarise(value.frameloop.update), + preRender: summarise(value.frameloop.preRender), + render: summarise(value.frameloop.render), + postRender: summarise(value.frameloop.postRender), + }, + animations: { + mainThread: summarise(value.animations.mainThread), + waapi: summarise(value.animations.waapi), + layout: summarise(value.animations.layout), + }, + layoutProjection: { + nodes: summarise(value.layoutProjection.nodes), + calculatedTargetDeltas: summarise(value.layoutProjection.calculatedTargetDeltas), + calculatedProjections: summarise(value.layoutProjection.calculatedProjections), + }, + }; + /** + * Convert the rate to FPS + */ + const { rate } = summary.frameloop; + rate.min = msToFps(rate.min); + rate.max = msToFps(rate.max); + rate.avg = msToFps(rate.avg); + [rate.min, rate.max] = [rate.max, rate.min]; + return summary; +} +function recordStats() { + if (statsBuffer.value) { + clearStatsBuffer(); + throw new Error("Stats are already being measured"); + } + const newStatsBuffer = statsBuffer; + newStatsBuffer.value = { + frameloop: { + setup: [], + rate: [], + read: [], + resolveKeyframes: [], + preUpdate: [], + update: [], + preRender: [], + render: [], + postRender: [], + }, + animations: { + mainThread: [], + waapi: [], + layout: [], + }, + layoutProjection: { + nodes: [], + calculatedTargetDeltas: [], + calculatedProjections: [], + }, + }; + newStatsBuffer.addProjectionMetrics = (metrics) => { + const { layoutProjection } = newStatsBuffer.value; + layoutProjection.nodes.push(metrics.nodes); + layoutProjection.calculatedTargetDeltas.push(metrics.calculatedTargetDeltas); + layoutProjection.calculatedProjections.push(metrics.calculatedProjections); + }; + frame.postRender(record, true); + return reportStats; +} + +export { recordStats }; diff --git a/node_modules/motion-dom/dist/es/utils/interpolate.mjs b/node_modules/motion-dom/dist/es/utils/interpolate.mjs new file mode 100644 index 00000000..26e952a0 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/interpolate.mjs @@ -0,0 +1,74 @@ +import { invariant, clamp, MotionGlobalConfig, noop, pipe, progress } from 'motion-utils'; +import { mix } from './mix/index.mjs'; + +function createMixers(output, ease, customMixer) { + const mixers = []; + const mixerFactory = customMixer || MotionGlobalConfig.mix || mix; + const numMixers = output.length - 1; + for (let i = 0; i < numMixers; i++) { + let mixer = mixerFactory(output[i], output[i + 1]); + if (ease) { + const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease; + mixer = pipe(easingFunction, mixer); + } + mixers.push(mixer); + } + return mixers; +} +/** + * Create a function that maps from a numerical input array to a generic output array. + * + * Accepts: + * - Numbers + * - Colors (hex, hsl, hsla, rgb, rgba) + * - Complex (combinations of one or more numbers or strings) + * + * ```jsx + * const mixColor = interpolate([0, 1], ['#fff', '#000']) + * + * mixColor(0.5) // 'rgba(128, 128, 128, 1)' + * ``` + * + * TODO Revisit this approach once we've moved to data models for values, + * probably not needed to pregenerate mixer functions. + * + * @public + */ +function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) { + const inputLength = input.length; + invariant(inputLength === output.length, "Both input and output ranges must be the same length"); + /** + * If we're only provided a single input, we can just make a function + * that returns the output. + */ + if (inputLength === 1) + return () => output[0]; + if (inputLength === 2 && output[0] === output[1]) + return () => output[1]; + const isZeroDeltaRange = input[0] === input[1]; + // If input runs highest -> lowest, reverse both arrays + if (input[0] > input[inputLength - 1]) { + input = [...input].reverse(); + output = [...output].reverse(); + } + const mixers = createMixers(output, ease, mixer); + const numMixers = mixers.length; + const interpolator = (v) => { + if (isZeroDeltaRange && v < input[0]) + return output[0]; + let i = 0; + if (numMixers > 1) { + for (; i < input.length - 2; i++) { + if (v < input[i + 1]) + break; + } + } + const progressInRange = progress(input[i], input[i + 1], v); + return mixers[i](progressInRange); + }; + return isClamp + ? (v) => interpolator(clamp(input[0], input[inputLength - 1], v)) + : interpolator; +} + +export { interpolate }; diff --git a/node_modules/motion-dom/dist/es/utils/is-html-element.mjs b/node_modules/motion-dom/dist/es/utils/is-html-element.mjs new file mode 100644 index 00000000..0e3a9d1f --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/is-html-element.mjs @@ -0,0 +1,11 @@ +import { isObject } from 'motion-utils'; + +/** + * Checks if an element is an HTML element in a way + * that works across iframes + */ +function isHTMLElement(element) { + return isObject(element) && "offsetHeight" in element; +} + +export { isHTMLElement }; diff --git a/node_modules/motion-dom/dist/es/utils/is-svg-element.mjs b/node_modules/motion-dom/dist/es/utils/is-svg-element.mjs new file mode 100644 index 00000000..d4386a86 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/is-svg-element.mjs @@ -0,0 +1,11 @@ +import { isObject } from 'motion-utils'; + +/** + * Checks if an element is an SVG element in a way + * that works across iframes + */ +function isSVGElement(element) { + return isObject(element) && "ownerSVGElement" in element; +} + +export { isSVGElement }; diff --git a/node_modules/motion-dom/dist/es/utils/is-svg-svg-element.mjs b/node_modules/motion-dom/dist/es/utils/is-svg-svg-element.mjs new file mode 100644 index 00000000..cd775c86 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/is-svg-svg-element.mjs @@ -0,0 +1,11 @@ +import { isSVGElement } from './is-svg-element.mjs'; + +/** + * Checks if an element is specifically an SVGSVGElement (the root SVG element) + * in a way that works across iframes + */ +function isSVGSVGElement(element) { + return isSVGElement(element) && element.tagName === "svg"; +} + +export { isSVGSVGElement }; diff --git a/node_modules/motion-dom/dist/es/utils/mix/color.mjs b/node_modules/motion-dom/dist/es/utils/mix/color.mjs new file mode 100644 index 00000000..0490f849 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/mix/color.mjs @@ -0,0 +1,47 @@ +import { warning } from 'motion-utils'; +import { hex } from '../../value/types/color/hex.mjs'; +import { hsla } from '../../value/types/color/hsla.mjs'; +import { hslaToRgba } from '../../value/types/color/hsla-to-rgba.mjs'; +import { rgba } from '../../value/types/color/rgba.mjs'; +import { mixImmediate } from './immediate.mjs'; +import { mixNumber } from './number.mjs'; + +// Linear color space blending +// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw +// Demonstrated http://codepen.io/osublake/pen/xGVVaN +const mixLinearColor = (from, to, v) => { + const fromExpo = from * from; + const expo = v * (to * to - fromExpo) + fromExpo; + return expo < 0 ? 0 : Math.sqrt(expo); +}; +const colorTypes = [hex, rgba, hsla]; +const getColorType = (v) => colorTypes.find((type) => type.test(v)); +function asRGBA(color) { + const type = getColorType(color); + warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`); + if (!Boolean(type)) + return false; + let model = type.parse(color); + if (type === hsla) { + // TODO Remove this cast - needed since Motion's stricter typing + model = hslaToRgba(model); + } + return model; +} +const mixColor = (from, to) => { + const fromRGBA = asRGBA(from); + const toRGBA = asRGBA(to); + if (!fromRGBA || !toRGBA) { + return mixImmediate(from, to); + } + const blended = { ...fromRGBA }; + return (v) => { + blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v); + blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v); + blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v); + blended.alpha = mixNumber(fromRGBA.alpha, toRGBA.alpha, v); + return rgba.transform(blended); + }; +}; + +export { mixColor, mixLinearColor }; diff --git a/node_modules/motion-dom/dist/es/utils/mix/complex.mjs b/node_modules/motion-dom/dist/es/utils/mix/complex.mjs new file mode 100644 index 00000000..53145d00 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/mix/complex.mjs @@ -0,0 +1,92 @@ +import { pipe, warning } from 'motion-utils'; +import { isCSSVariableToken } from '../../animation/utils/is-css-variable.mjs'; +import { color } from '../../value/types/color/index.mjs'; +import { complex, analyseComplexValue } from '../../value/types/complex/index.mjs'; +import { mixColor } from './color.mjs'; +import { mixImmediate } from './immediate.mjs'; +import { mixNumber as mixNumber$1 } from './number.mjs'; +import { invisibleValues, mixVisibility } from './visibility.mjs'; + +function mixNumber(a, b) { + return (p) => mixNumber$1(a, b, p); +} +function getMixer(a) { + if (typeof a === "number") { + return mixNumber; + } + else if (typeof a === "string") { + return isCSSVariableToken(a) + ? mixImmediate + : color.test(a) + ? mixColor + : mixComplex; + } + else if (Array.isArray(a)) { + return mixArray; + } + else if (typeof a === "object") { + return color.test(a) ? mixColor : mixObject; + } + return mixImmediate; +} +function mixArray(a, b) { + const output = [...a]; + const numValues = output.length; + const blendValue = a.map((v, i) => getMixer(v)(v, b[i])); + return (p) => { + for (let i = 0; i < numValues; i++) { + output[i] = blendValue[i](p); + } + return output; + }; +} +function mixObject(a, b) { + const output = { ...a, ...b }; + const blendValue = {}; + for (const key in output) { + if (a[key] !== undefined && b[key] !== undefined) { + blendValue[key] = getMixer(a[key])(a[key], b[key]); + } + } + return (v) => { + for (const key in blendValue) { + output[key] = blendValue[key](v); + } + return output; + }; +} +function matchOrder(origin, target) { + const orderedOrigin = []; + const pointers = { color: 0, var: 0, number: 0 }; + for (let i = 0; i < target.values.length; i++) { + const type = target.types[i]; + const originIndex = origin.indexes[type][pointers[type]]; + const originValue = origin.values[originIndex] ?? 0; + orderedOrigin[i] = originValue; + pointers[type]++; + } + return orderedOrigin; +} +const mixComplex = (origin, target) => { + const template = complex.createTransformer(target); + const originStats = analyseComplexValue(origin); + const targetStats = analyseComplexValue(target); + const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length && + originStats.indexes.color.length === targetStats.indexes.color.length && + originStats.indexes.number.length >= targetStats.indexes.number.length; + if (canInterpolate) { + if ((invisibleValues.has(origin) && + !targetStats.values.length) || + (invisibleValues.has(target) && + !originStats.values.length)) { + return mixVisibility(origin, target); + } + return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template); + } + else { + warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`); + return mixImmediate(origin, target); + } +}; + +export { getMixer, mixArray, mixComplex, mixObject }; diff --git a/node_modules/motion-dom/dist/es/utils/mix/immediate.mjs b/node_modules/motion-dom/dist/es/utils/mix/immediate.mjs new file mode 100644 index 00000000..baf7241a --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/mix/immediate.mjs @@ -0,0 +1,5 @@ +function mixImmediate(a, b) { + return (p) => (p > 0 ? b : a); +} + +export { mixImmediate }; diff --git a/node_modules/motion-dom/dist/es/utils/mix/index.mjs b/node_modules/motion-dom/dist/es/utils/mix/index.mjs new file mode 100644 index 00000000..e4839410 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/mix/index.mjs @@ -0,0 +1,14 @@ +import { getMixer } from './complex.mjs'; +import { mixNumber } from './number.mjs'; + +function mix(from, to, p) { + if (typeof from === "number" && + typeof to === "number" && + typeof p === "number") { + return mixNumber(from, to, p); + } + const mixer = getMixer(from); + return mixer(from, to); +} + +export { mix }; diff --git a/node_modules/motion-dom/dist/es/utils/mix/number.mjs b/node_modules/motion-dom/dist/es/utils/mix/number.mjs new file mode 100644 index 00000000..bbdbc411 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/mix/number.mjs @@ -0,0 +1,26 @@ +/* + Value in range from progress + + Given a lower limit and an upper limit, we return the value within + that range as expressed by progress (usually a number from 0 to 1) + + So progress = 0.5 would change + + from -------- to + + to + + from ---- to + + E.g. from = 10, to = 20, progress = 0.5 => 15 + + @param [number]: Lower limit of range + @param [number]: Upper limit of range + @param [number]: The progress between lower and upper limits expressed 0-1 + @return [number]: Value as calculated from progress within range (not limited within range) +*/ +const mixNumber = (from, to, progress) => { + return from + (to - from) * progress; +}; + +export { mixNumber }; diff --git a/node_modules/motion-dom/dist/es/utils/mix/visibility.mjs b/node_modules/motion-dom/dist/es/utils/mix/visibility.mjs new file mode 100644 index 00000000..d96cd8dc --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/mix/visibility.mjs @@ -0,0 +1,16 @@ +const invisibleValues = new Set(["none", "hidden"]); +/** + * Returns a function that, when provided a progress value between 0 and 1, + * will return the "none" or "hidden" string only when the progress is that of + * the origin or target. + */ +function mixVisibility(origin, target) { + if (invisibleValues.has(origin)) { + return (p) => (p <= 0 ? origin : target); + } + else { + return (p) => (p >= 1 ? target : origin); + } +} + +export { invisibleValues, mixVisibility }; diff --git a/node_modules/motion-dom/dist/es/utils/resolve-elements.mjs b/node_modules/motion-dom/dist/es/utils/resolve-elements.mjs new file mode 100644 index 00000000..32811829 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/resolve-elements.mjs @@ -0,0 +1,17 @@ +function resolveElements(elementOrSelector, scope, selectorCache) { + if (elementOrSelector instanceof EventTarget) { + return [elementOrSelector]; + } + else if (typeof elementOrSelector === "string") { + let root = document; + if (scope) { + root = scope.current; + } + const elements = selectorCache?.[elementOrSelector] ?? + root.querySelectorAll(elementOrSelector); + return elements ? Array.from(elements) : []; + } + return Array.from(elementOrSelector); +} + +export { resolveElements }; diff --git a/node_modules/motion-dom/dist/es/utils/stagger.mjs b/node_modules/motion-dom/dist/es/utils/stagger.mjs new file mode 100644 index 00000000..c10eb35c --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/stagger.mjs @@ -0,0 +1,26 @@ +import { easingDefinitionToFunction } from 'motion-utils'; + +function getOriginIndex(from, total) { + if (from === "first") { + return 0; + } + else { + const lastIndex = total - 1; + return from === "last" ? lastIndex : lastIndex / 2; + } +} +function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) { + return (i, total) => { + const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total); + const distance = Math.abs(fromIndex - i); + let delay = duration * distance; + if (ease) { + const maxDelay = total * duration; + const easingFunction = easingDefinitionToFunction(ease); + delay = easingFunction(delay / maxDelay) * maxDelay; + } + return startDelay + delay; + }; +} + +export { getOriginIndex, stagger }; diff --git a/node_modules/motion-dom/dist/es/utils/supports/flags.mjs b/node_modules/motion-dom/dist/es/utils/supports/flags.mjs new file mode 100644 index 00000000..6709287a --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/supports/flags.mjs @@ -0,0 +1,7 @@ +/** + * Add the ability for test suites to manually set support flags + * to better test more environments. + */ +const supportsFlags = {}; + +export { supportsFlags }; diff --git a/node_modules/motion-dom/dist/es/utils/supports/linear-easing.mjs b/node_modules/motion-dom/dist/es/utils/supports/linear-easing.mjs new file mode 100644 index 00000000..eeec14c9 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/supports/linear-easing.mjs @@ -0,0 +1,15 @@ +import { memoSupports } from './memo.mjs'; + +const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => { + try { + document + .createElement("div") + .animate({ opacity: 0 }, { easing: "linear(0, 1)" }); + } + catch (e) { + return false; + } + return true; +}, "linearEasing"); + +export { supportsLinearEasing }; diff --git a/node_modules/motion-dom/dist/es/utils/supports/memo.mjs b/node_modules/motion-dom/dist/es/utils/supports/memo.mjs new file mode 100644 index 00000000..c011dabf --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/supports/memo.mjs @@ -0,0 +1,9 @@ +import { memo } from 'motion-utils'; +import { supportsFlags } from './flags.mjs'; + +function memoSupports(callback, supportsFlag) { + const memoized = memo(callback); + return () => supportsFlags[supportsFlag] ?? memoized(); +} + +export { memoSupports }; diff --git a/node_modules/motion-dom/dist/es/utils/supports/scroll-timeline.mjs b/node_modules/motion-dom/dist/es/utils/supports/scroll-timeline.mjs new file mode 100644 index 00000000..72c25328 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/supports/scroll-timeline.mjs @@ -0,0 +1,5 @@ +import { memo } from 'motion-utils'; + +const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined); + +export { supportsScrollTimeline }; diff --git a/node_modules/motion-dom/dist/es/utils/transform.mjs b/node_modules/motion-dom/dist/es/utils/transform.mjs new file mode 100644 index 00000000..6f6b3a61 --- /dev/null +++ b/node_modules/motion-dom/dist/es/utils/transform.mjs @@ -0,0 +1,14 @@ +import { interpolate } from './interpolate.mjs'; + +function transform(...args) { + const useImmediate = !Array.isArray(args[0]); + const argOffset = useImmediate ? 0 : -1; + const inputValue = args[0 + argOffset]; + const inputRange = args[1 + argOffset]; + const outputRange = args[2 + argOffset]; + const options = args[3 + argOffset]; + const interpolator = interpolate(inputRange, outputRange, options); + return useImmediate ? interpolator(inputValue) : interpolator; +} + +export { transform }; diff --git a/node_modules/motion-dom/dist/es/value/index.mjs b/node_modules/motion-dom/dist/es/value/index.mjs new file mode 100644 index 00000000..e2ffe9fe --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/index.mjs @@ -0,0 +1,327 @@ +import { warnOnce, SubscriptionManager, velocityPerSecond } from 'motion-utils'; +import { time } from '../frameloop/sync-time.mjs'; +import { frame } from '../frameloop/frame.mjs'; + +/** + * Maximum time between the value of two frames, beyond which we + * assume the velocity has since been 0. + */ +const MAX_VELOCITY_DELTA = 30; +const isFloat = (value) => { + return !isNaN(parseFloat(value)); +}; +const collectMotionValues = { + current: undefined, +}; +/** + * `MotionValue` is used to track the state and velocity of motion values. + * + * @public + */ +class MotionValue { + /** + * @param init - The initiating value + * @param config - Optional configuration options + * + * - `transformer`: A function to transform incoming values with. + */ + constructor(init, options = {}) { + /** + * Tracks whether this value can output a velocity. Currently this is only true + * if the value is numerical, but we might be able to widen the scope here and support + * other value types. + * + * @internal + */ + this.canTrackVelocity = null; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + this.updateAndNotify = (v, render = true) => { + const currentTime = time.now(); + /** + * If we're updating the value during another frame or eventloop + * than the previous frame, then the we set the previous frame value + * to current. + */ + if (this.updatedAt !== currentTime) { + this.setPrevFrameValue(); + } + this.prev = this.current; + this.setCurrent(v); + // Update update subscribers + if (this.current !== this.prev) { + this.events.change?.notify(this.current); + if (this.dependents) { + for (const dependent of this.dependents) { + dependent.dirty(); + } + } + } + // Update render subscribers + if (render) { + this.events.renderRequest?.notify(this.current); + } + }; + this.hasAnimated = false; + this.setCurrent(init); + this.owner = options.owner; + } + setCurrent(current) { + this.current = current; + this.updatedAt = time.now(); + if (this.canTrackVelocity === null && current !== undefined) { + this.canTrackVelocity = isFloat(this.current); + } + } + setPrevFrameValue(prevFrameValue = this.current) { + this.prevFrameValue = prevFrameValue; + this.prevUpdatedAt = this.updatedAt; + } + /** + * Adds a function that will be notified when the `MotionValue` is updated. + * + * It returns a function that, when called, will cancel the subscription. + * + * When calling `onChange` inside a React component, it should be wrapped with the + * `useEffect` hook. As it returns an unsubscribe function, this should be returned + * from the `useEffect` function to ensure you don't add duplicate subscribers.. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const y = useMotionValue(0) + * const opacity = useMotionValue(1) + * + * useEffect(() => { + * function updateOpacity() { + * const maxXY = Math.max(x.get(), y.get()) + * const newOpacity = transform(maxXY, [0, 100], [1, 0]) + * opacity.set(newOpacity) + * } + * + * const unsubscribeX = x.on("change", updateOpacity) + * const unsubscribeY = y.on("change", updateOpacity) + * + * return () => { + * unsubscribeX() + * unsubscribeY() + * } + * }, []) + * + * return + * } + * ``` + * + * @param subscriber - A function that receives the latest value. + * @returns A function that, when called, will cancel this subscription. + * + * @deprecated + */ + onChange(subscription) { + if (process.env.NODE_ENV !== "production") { + warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`); + } + return this.on("change", subscription); + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new SubscriptionManager(); + } + const unsubscribe = this.events[eventName].add(callback); + if (eventName === "change") { + return () => { + unsubscribe(); + /** + * If we have no more change listeners by the start + * of the next frame, stop active animations. + */ + frame.read(() => { + if (!this.events.change.getSize()) { + this.stop(); + } + }); + }; + } + return unsubscribe; + } + clearListeners() { + for (const eventManagers in this.events) { + this.events[eventManagers].clear(); + } + } + /** + * Attaches a passive effect to the `MotionValue`. + */ + attach(passiveEffect, stopPassiveEffect) { + this.passiveEffect = passiveEffect; + this.stopPassiveEffect = stopPassiveEffect; + } + /** + * Sets the state of the `MotionValue`. + * + * @remarks + * + * ```jsx + * const x = useMotionValue(0) + * x.set(10) + * ``` + * + * @param latest - Latest value to set. + * @param render - Whether to notify render subscribers. Defaults to `true` + * + * @public + */ + set(v, render = true) { + if (!render || !this.passiveEffect) { + this.updateAndNotify(v, render); + } + else { + this.passiveEffect(v, this.updateAndNotify); + } + } + setWithVelocity(prev, current, delta) { + this.set(current); + this.prev = undefined; + this.prevFrameValue = prev; + this.prevUpdatedAt = this.updatedAt - delta; + } + /** + * Set the state of the `MotionValue`, stopping any active animations, + * effects, and resets velocity to `0`. + */ + jump(v, endAnimation = true) { + this.updateAndNotify(v); + this.prev = v; + this.prevUpdatedAt = this.prevFrameValue = undefined; + endAnimation && this.stop(); + if (this.stopPassiveEffect) + this.stopPassiveEffect(); + } + dirty() { + this.events.change?.notify(this.current); + } + addDependent(dependent) { + if (!this.dependents) { + this.dependents = new Set(); + } + this.dependents.add(dependent); + } + removeDependent(dependent) { + if (this.dependents) { + this.dependents.delete(dependent); + } + } + /** + * Returns the latest state of `MotionValue` + * + * @returns - The latest state of `MotionValue` + * + * @public + */ + get() { + if (collectMotionValues.current) { + collectMotionValues.current.push(this); + } + return this.current; + } + /** + * @public + */ + getPrevious() { + return this.prev; + } + /** + * Returns the latest velocity of `MotionValue` + * + * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. + * + * @public + */ + getVelocity() { + const currentTime = time.now(); + if (!this.canTrackVelocity || + this.prevFrameValue === undefined || + currentTime - this.updatedAt > MAX_VELOCITY_DELTA) { + return 0; + } + const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA); + // Casts because of parseFloat's poor typing + return velocityPerSecond(parseFloat(this.current) - + parseFloat(this.prevFrameValue), delta); + } + /** + * Registers a new animation to control this `MotionValue`. Only one + * animation can drive a `MotionValue` at one time. + * + * ```jsx + * value.start() + * ``` + * + * @param animation - A function that starts the provided animation + */ + start(startAnimation) { + this.stop(); + return new Promise((resolve) => { + this.hasAnimated = true; + this.animation = startAnimation(resolve); + if (this.events.animationStart) { + this.events.animationStart.notify(); + } + }).then(() => { + if (this.events.animationComplete) { + this.events.animationComplete.notify(); + } + this.clearAnimation(); + }); + } + /** + * Stop the currently active animation. + * + * @public + */ + stop() { + if (this.animation) { + this.animation.stop(); + if (this.events.animationCancel) { + this.events.animationCancel.notify(); + } + } + this.clearAnimation(); + } + /** + * Returns `true` if this value is currently animating. + * + * @public + */ + isAnimating() { + return !!this.animation; + } + clearAnimation() { + delete this.animation; + } + /** + * Destroy and clean up subscribers to this `MotionValue`. + * + * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically + * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually + * created a `MotionValue` via the `motionValue` function. + * + * @public + */ + destroy() { + this.dependents?.clear(); + this.events.destroy?.notify(); + this.clearListeners(); + this.stop(); + if (this.stopPassiveEffect) { + this.stopPassiveEffect(); + } + } +} +function motionValue(init, options) { + return new MotionValue(init, options); +} + +export { MotionValue, collectMotionValues, motionValue }; diff --git a/node_modules/motion-dom/dist/es/value/map-value.mjs b/node_modules/motion-dom/dist/es/value/map-value.mjs new file mode 100644 index 00000000..09e1996c --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/map-value.mjs @@ -0,0 +1,46 @@ +import { transform } from '../utils/transform.mjs'; +import { transformValue } from './transform-value.mjs'; + +/** + * Create a `MotionValue` that maps the output of another `MotionValue` by + * mapping it from one range of values into another. + * + * @remarks + * + * Given an input range of `[-200, -100, 100, 200]` and an output range of + * `[0, 1, 1, 0]`, the returned `MotionValue` will: + * + * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`. + * - When provided a value between `-100` and `100`, will return `1`. + * - When provided a value between `100` and `200`, will return a value between `1` and `0` + * + * The input range must be a linear series of numbers. The output range + * can be any value type supported by Motion: numbers, colors, shadows, etc. + * + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * const x = motionValue(0) + * const xRange = [-200, -100, 100, 200] + * const opacityRange = [0, 1, 1, 0] + * const opacity = mapValue(x, xRange, opacityRange) + * ``` + * + * @param inputValue - `MotionValue` + * @param inputRange - A linear series of numbers (either all increasing or decreasing) + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - + * + * - clamp: boolean. Clamp values to within the given range. Defaults to `true` + * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each. + * + * @returns `MotionValue` + * + * @public + */ +function mapValue(inputValue, inputRange, outputRange, options) { + const map = transform(inputRange, outputRange, options); + return transformValue(() => map(inputValue.get())); +} + +export { mapValue }; diff --git a/node_modules/motion-dom/dist/es/value/spring-value.mjs b/node_modules/motion-dom/dist/es/value/spring-value.mjs new file mode 100644 index 00000000..17dbd639 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/spring-value.mjs @@ -0,0 +1,72 @@ +import { motionValue } from './index.mjs'; +import { JSAnimation } from '../animation/JSAnimation.mjs'; +import { isMotionValue } from './utils/is-motion-value.mjs'; +import { frame } from '../frameloop/frame.mjs'; + +/** + * Create a `MotionValue` that animates to its latest value using a spring. + * Can either be a value or track another `MotionValue`. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ +function springValue(source, options) { + const initialValue = isMotionValue(source) ? source.get() : source; + const value = motionValue(initialValue); + attachSpring(value, source, options); + return value; +} +function attachSpring(value, source, options) { + const initialValue = value.get(); + let activeAnimation = null; + let latestValue = initialValue; + let latestSetter; + const unit = typeof initialValue === "string" + ? initialValue.replace(/[\d.-]/g, "") + : undefined; + const stopAnimation = () => { + if (activeAnimation) { + activeAnimation.stop(); + activeAnimation = null; + } + }; + const startAnimation = () => { + stopAnimation(); + activeAnimation = new JSAnimation({ + keyframes: [asNumber(value.get()), asNumber(latestValue)], + velocity: value.getVelocity(), + type: "spring", + restDelta: 0.001, + restSpeed: 0.01, + ...options, + onUpdate: latestSetter, + }); + }; + value.attach((v, set) => { + latestValue = v; + latestSetter = (latest) => set(parseValue(latest, unit)); + frame.postRender(startAnimation); + return value.get(); + }, stopAnimation); + let unsubscribe = undefined; + if (isMotionValue(source)) { + unsubscribe = source.on("change", (v) => value.set(parseValue(v, unit))); + value.on("destroy", unsubscribe); + } + return unsubscribe; +} +function parseValue(v, unit) { + return unit ? v + unit : v; +} +function asNumber(v) { + return typeof v === "number" ? v : parseFloat(v); +} + +export { attachSpring, springValue }; diff --git a/node_modules/motion-dom/dist/es/value/subscribe-value.mjs b/node_modules/motion-dom/dist/es/value/subscribe-value.mjs new file mode 100644 index 00000000..ccea4048 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/subscribe-value.mjs @@ -0,0 +1,13 @@ +import { cancelFrame, frame } from '../frameloop/frame.mjs'; + +function subscribeValue(inputValues, outputValue, getLatest) { + const update = () => outputValue.set(getLatest()); + const scheduleUpdate = () => frame.preRender(update, false, true); + const subscriptions = inputValues.map((v) => v.on("change", scheduleUpdate)); + outputValue.on("destroy", () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + cancelFrame(update); + }); +} + +export { subscribeValue }; diff --git a/node_modules/motion-dom/dist/es/value/transform-value.mjs b/node_modules/motion-dom/dist/es/value/transform-value.mjs new file mode 100644 index 00000000..c17af460 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/transform-value.mjs @@ -0,0 +1,35 @@ +import { collectMotionValues, motionValue } from './index.mjs'; +import { subscribeValue } from './subscribe-value.mjs'; + +/** + * Create a `MotionValue` that transforms the output of other `MotionValue`s by + * passing their latest values through a transform function. + * + * Whenever a `MotionValue` referred to in the provided function is updated, + * it will be re-evaluated. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ +function transformValue(transform) { + const collectedValues = []; + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * inside transform will be saved into this array. + */ + collectMotionValues.current = collectedValues; + const initialValue = transform(); + collectMotionValues.current = undefined; + const value = motionValue(initialValue); + subscribeValue(collectedValues, value, transform); + return value; +} + +export { transformValue }; diff --git a/node_modules/motion-dom/dist/es/value/types/auto.mjs b/node_modules/motion-dom/dist/es/value/types/auto.mjs new file mode 100644 index 00000000..29243876 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/auto.mjs @@ -0,0 +1,9 @@ +/** + * ValueType for "auto" + */ +const auto = { + test: (v) => v === "auto", + parse: (v) => v, +}; + +export { auto }; diff --git a/node_modules/motion-dom/dist/es/value/types/color/hex.mjs b/node_modules/motion-dom/dist/es/value/types/color/hex.mjs new file mode 100644 index 00000000..3fefcd35 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/color/hex.mjs @@ -0,0 +1,40 @@ +import { rgba } from './rgba.mjs'; +import { isColorString } from './utils.mjs'; + +function parseHex(v) { + let r = ""; + let g = ""; + let b = ""; + let a = ""; + // If we have 6 characters, ie #FF0000 + if (v.length > 5) { + r = v.substring(1, 3); + g = v.substring(3, 5); + b = v.substring(5, 7); + a = v.substring(7, 9); + // Or we have 3 characters, ie #F00 + } + else { + r = v.substring(1, 2); + g = v.substring(2, 3); + b = v.substring(3, 4); + a = v.substring(4, 5); + r += r; + g += g; + b += b; + a += a; + } + return { + red: parseInt(r, 16), + green: parseInt(g, 16), + blue: parseInt(b, 16), + alpha: a ? parseInt(a, 16) / 255 : 1, + }; +} +const hex = { + test: /*@__PURE__*/ isColorString("#"), + parse: parseHex, + transform: rgba.transform, +}; + +export { hex }; diff --git a/node_modules/motion-dom/dist/es/value/types/color/hsla-to-rgba.mjs b/node_modules/motion-dom/dist/es/value/types/color/hsla-to-rgba.mjs new file mode 100644 index 00000000..0e09009f --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/color/hsla-to-rgba.mjs @@ -0,0 +1,42 @@ +// Adapted from https://gist.github.com/mjackson/5311256 +function hueToRgb(p, q, t) { + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < 1 / 6) + return p + (q - p) * 6 * t; + if (t < 1 / 2) + return q; + if (t < 2 / 3) + return p + (q - p) * (2 / 3 - t) * 6; + return p; +} +function hslaToRgba({ hue, saturation, lightness, alpha }) { + hue /= 360; + saturation /= 100; + lightness /= 100; + let red = 0; + let green = 0; + let blue = 0; + if (!saturation) { + red = green = blue = lightness; + } + else { + const q = lightness < 0.5 + ? lightness * (1 + saturation) + : lightness + saturation - lightness * saturation; + const p = 2 * lightness - q; + red = hueToRgb(p, q, hue + 1 / 3); + green = hueToRgb(p, q, hue); + blue = hueToRgb(p, q, hue - 1 / 3); + } + return { + red: Math.round(red * 255), + green: Math.round(green * 255), + blue: Math.round(blue * 255), + alpha, + }; +} + +export { hslaToRgba }; diff --git a/node_modules/motion-dom/dist/es/value/types/color/hsla.mjs b/node_modules/motion-dom/dist/es/value/types/color/hsla.mjs new file mode 100644 index 00000000..5af4c91a --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/color/hsla.mjs @@ -0,0 +1,22 @@ +import { alpha } from '../numbers/index.mjs'; +import { percent } from '../numbers/units.mjs'; +import { sanitize } from '../utils/sanitize.mjs'; +import { isColorString, splitColor } from './utils.mjs'; + +const hsla = { + test: /*@__PURE__*/ isColorString("hsl", "hue"), + parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"), + transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { + return ("hsla(" + + Math.round(hue) + + ", " + + percent.transform(sanitize(saturation)) + + ", " + + percent.transform(sanitize(lightness)) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")"); + }, +}; + +export { hsla }; diff --git a/node_modules/motion-dom/dist/es/value/types/color/index.mjs b/node_modules/motion-dom/dist/es/value/types/color/index.mjs new file mode 100644 index 00000000..2ef7a106 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/color/index.mjs @@ -0,0 +1,32 @@ +import { hex } from './hex.mjs'; +import { hsla } from './hsla.mjs'; +import { rgba } from './rgba.mjs'; + +const color = { + test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v), + parse: (v) => { + if (rgba.test(v)) { + return rgba.parse(v); + } + else if (hsla.test(v)) { + return hsla.parse(v); + } + else { + return hex.parse(v); + } + }, + transform: (v) => { + return typeof v === "string" + ? v + : v.hasOwnProperty("red") + ? rgba.transform(v) + : hsla.transform(v); + }, + getAnimatableNone: (v) => { + const parsed = color.parse(v); + parsed.alpha = 0; + return color.transform(parsed); + }, +}; + +export { color }; diff --git a/node_modules/motion-dom/dist/es/value/types/color/rgba.mjs b/node_modules/motion-dom/dist/es/value/types/color/rgba.mjs new file mode 100644 index 00000000..3f316a4d --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/color/rgba.mjs @@ -0,0 +1,25 @@ +import { clamp } from 'motion-utils'; +import { number, alpha } from '../numbers/index.mjs'; +import { sanitize } from '../utils/sanitize.mjs'; +import { isColorString, splitColor } from './utils.mjs'; + +const clampRgbUnit = (v) => clamp(0, 255, v); +const rgbUnit = { + ...number, + transform: (v) => Math.round(clampRgbUnit(v)), +}; +const rgba = { + test: /*@__PURE__*/ isColorString("rgb", "red"), + parse: /*@__PURE__*/ splitColor("red", "green", "blue"), + transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + + rgbUnit.transform(red) + + ", " + + rgbUnit.transform(green) + + ", " + + rgbUnit.transform(blue) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")", +}; + +export { rgbUnit, rgba }; diff --git a/node_modules/motion-dom/dist/es/value/types/color/utils.mjs b/node_modules/motion-dom/dist/es/value/types/color/utils.mjs new file mode 100644 index 00000000..0507b049 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/color/utils.mjs @@ -0,0 +1,29 @@ +import { floatRegex } from '../utils/float-regex.mjs'; +import { isNullish } from '../utils/is-nullish.mjs'; +import { singleColorRegex } from '../utils/single-color-regex.mjs'; + +/** + * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000, + * but false if a number or multiple colors + */ +const isColorString = (type, testProp) => (v) => { + return Boolean((typeof v === "string" && + singleColorRegex.test(v) && + v.startsWith(type)) || + (testProp && + !isNullish(v) && + Object.prototype.hasOwnProperty.call(v, testProp))); +}; +const splitColor = (aName, bName, cName) => (v) => { + if (typeof v !== "string") + return v; + const [a, b, c, alpha] = v.match(floatRegex); + return { + [aName]: parseFloat(a), + [bName]: parseFloat(b), + [cName]: parseFloat(c), + alpha: alpha !== undefined ? parseFloat(alpha) : 1, + }; +}; + +export { isColorString, splitColor }; diff --git a/node_modules/motion-dom/dist/es/value/types/complex/filter.mjs b/node_modules/motion-dom/dist/es/value/types/complex/filter.mjs new file mode 100644 index 00000000..97001df4 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/complex/filter.mjs @@ -0,0 +1,30 @@ +import { complex } from './index.mjs'; +import { floatRegex } from '../utils/float-regex.mjs'; + +/** + * Properties that should default to 1 or 100% + */ +const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]); +function applyDefaultFilter(v) { + const [name, value] = v.slice(0, -1).split("("); + if (name === "drop-shadow") + return v; + const [number] = value.match(floatRegex) || []; + if (!number) + return v; + const unit = value.replace(number, ""); + let defaultValue = maxDefaults.has(name) ? 1 : 0; + if (number !== value) + defaultValue *= 100; + return name + "(" + defaultValue + unit + ")"; +} +const functionRegex = /\b([a-z-]*)\(.*?\)/gu; +const filter = { + ...complex, + getAnimatableNone: (v) => { + const functions = v.match(functionRegex); + return functions ? functions.map(applyDefaultFilter).join(" ") : v; + }, +}; + +export { filter }; diff --git a/node_modules/motion-dom/dist/es/value/types/complex/index.mjs b/node_modules/motion-dom/dist/es/value/types/complex/index.mjs new file mode 100644 index 00000000..2677293a --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/complex/index.mjs @@ -0,0 +1,91 @@ +import { color } from '../color/index.mjs'; +import { colorRegex } from '../utils/color-regex.mjs'; +import { floatRegex } from '../utils/float-regex.mjs'; +import { sanitize } from '../utils/sanitize.mjs'; + +function test(v) { + return (isNaN(v) && + typeof v === "string" && + (v.match(floatRegex)?.length || 0) + + (v.match(colorRegex)?.length || 0) > + 0); +} +const NUMBER_TOKEN = "number"; +const COLOR_TOKEN = "color"; +const VAR_TOKEN = "var"; +const VAR_FUNCTION_TOKEN = "var("; +const SPLIT_TOKEN = "${}"; +// this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex` +const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu; +function analyseComplexValue(value) { + const originalValue = value.toString(); + const values = []; + const indexes = { + color: [], + number: [], + var: [], + }; + const types = []; + let i = 0; + const tokenised = originalValue.replace(complexRegex, (parsedValue) => { + if (color.test(parsedValue)) { + indexes.color.push(i); + types.push(COLOR_TOKEN); + values.push(color.parse(parsedValue)); + } + else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) { + indexes.var.push(i); + types.push(VAR_TOKEN); + values.push(parsedValue); + } + else { + indexes.number.push(i); + types.push(NUMBER_TOKEN); + values.push(parseFloat(parsedValue)); + } + ++i; + return SPLIT_TOKEN; + }); + const split = tokenised.split(SPLIT_TOKEN); + return { values, split, indexes, types }; +} +function parseComplexValue(v) { + return analyseComplexValue(v).values; +} +function createTransformer(source) { + const { split, types } = analyseComplexValue(source); + const numSections = split.length; + return (v) => { + let output = ""; + for (let i = 0; i < numSections; i++) { + output += split[i]; + if (v[i] !== undefined) { + const type = types[i]; + if (type === NUMBER_TOKEN) { + output += sanitize(v[i]); + } + else if (type === COLOR_TOKEN) { + output += color.transform(v[i]); + } + else { + output += v[i]; + } + } + } + return output; + }; +} +const convertNumbersToZero = (v) => typeof v === "number" ? 0 : color.test(v) ? color.getAnimatableNone(v) : v; +function getAnimatableNone(v) { + const parsed = parseComplexValue(v); + const transformer = createTransformer(v); + return transformer(parsed.map(convertNumbersToZero)); +} +const complex = { + test, + parse: parseComplexValue, + createTransformer, + getAnimatableNone, +}; + +export { analyseComplexValue, complex }; diff --git a/node_modules/motion-dom/dist/es/value/types/dimensions.mjs b/node_modules/motion-dom/dist/es/value/types/dimensions.mjs new file mode 100644 index 00000000..286ceaf0 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/dimensions.mjs @@ -0,0 +1,15 @@ +import { auto } from './auto.mjs'; +import { number } from './numbers/index.mjs'; +import { px, percent, degrees, vw, vh } from './numbers/units.mjs'; +import { testValueType } from './test.mjs'; + +/** + * A list of value types commonly used for dimensions + */ +const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto]; +/** + * Tests a dimensional value against the list of dimension ValueTypes + */ +const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v)); + +export { dimensionValueTypes, findDimensionValueType }; diff --git a/node_modules/motion-dom/dist/es/value/types/int.mjs b/node_modules/motion-dom/dist/es/value/types/int.mjs new file mode 100644 index 00000000..a18e9f66 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/int.mjs @@ -0,0 +1,8 @@ +import { number } from './numbers/index.mjs'; + +const int = { + ...number, + transform: Math.round, +}; + +export { int }; diff --git a/node_modules/motion-dom/dist/es/value/types/maps/defaults.mjs b/node_modules/motion-dom/dist/es/value/types/maps/defaults.mjs new file mode 100644 index 00000000..ae266718 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/maps/defaults.mjs @@ -0,0 +1,30 @@ +import { color } from '../color/index.mjs'; +import { filter } from '../complex/filter.mjs'; +import { numberValueTypes } from './number.mjs'; + +/** + * A map of default value types for common values + */ +const defaultValueTypes = { + ...numberValueTypes, + // Color props + color, + backgroundColor: color, + outlineColor: color, + fill: color, + stroke: color, + // Border props + borderColor: color, + borderTopColor: color, + borderRightColor: color, + borderBottomColor: color, + borderLeftColor: color, + filter, + WebkitFilter: filter, +}; +/** + * Gets the default ValueType for the provided value key + */ +const getDefaultValueType = (key) => defaultValueTypes[key]; + +export { defaultValueTypes, getDefaultValueType }; diff --git a/node_modules/motion-dom/dist/es/value/types/maps/number.mjs b/node_modules/motion-dom/dist/es/value/types/maps/number.mjs new file mode 100644 index 00000000..de0e933e --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/maps/number.mjs @@ -0,0 +1,50 @@ +import { int } from '../int.mjs'; +import { alpha } from '../numbers/index.mjs'; +import { px } from '../numbers/units.mjs'; +import { transformValueTypes } from './transform.mjs'; + +const numberValueTypes = { + // Border props + borderWidth: px, + borderTopWidth: px, + borderRightWidth: px, + borderBottomWidth: px, + borderLeftWidth: px, + borderRadius: px, + radius: px, + borderTopLeftRadius: px, + borderTopRightRadius: px, + borderBottomRightRadius: px, + borderBottomLeftRadius: px, + // Positioning props + width: px, + maxWidth: px, + height: px, + maxHeight: px, + top: px, + right: px, + bottom: px, + left: px, + // Spacing props + padding: px, + paddingTop: px, + paddingRight: px, + paddingBottom: px, + paddingLeft: px, + margin: px, + marginTop: px, + marginRight: px, + marginBottom: px, + marginLeft: px, + // Misc + backgroundPositionX: px, + backgroundPositionY: px, + ...transformValueTypes, + zIndex: int, + // SVG + fillOpacity: alpha, + strokeOpacity: alpha, + numOctaves: int, +}; + +export { numberValueTypes }; diff --git a/node_modules/motion-dom/dist/es/value/types/maps/transform.mjs b/node_modules/motion-dom/dist/es/value/types/maps/transform.mjs new file mode 100644 index 00000000..26d0c500 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/maps/transform.mjs @@ -0,0 +1,31 @@ +import { scale, alpha } from '../numbers/index.mjs'; +import { degrees, px, progressPercentage } from '../numbers/units.mjs'; + +const transformValueTypes = { + rotate: degrees, + rotateX: degrees, + rotateY: degrees, + rotateZ: degrees, + scale, + scaleX: scale, + scaleY: scale, + scaleZ: scale, + skew: degrees, + skewX: degrees, + skewY: degrees, + distance: px, + translateX: px, + translateY: px, + translateZ: px, + x: px, + y: px, + z: px, + perspective: px, + transformPerspective: px, + opacity: alpha, + originX: progressPercentage, + originY: progressPercentage, + originZ: px, +}; + +export { transformValueTypes }; diff --git a/node_modules/motion-dom/dist/es/value/types/numbers/index.mjs b/node_modules/motion-dom/dist/es/value/types/numbers/index.mjs new file mode 100644 index 00000000..ee86b4e5 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/numbers/index.mjs @@ -0,0 +1,17 @@ +import { clamp } from 'motion-utils'; + +const number = { + test: (v) => typeof v === "number", + parse: parseFloat, + transform: (v) => v, +}; +const alpha = { + ...number, + transform: (v) => clamp(0, 1, v), +}; +const scale = { + ...number, + default: 1, +}; + +export { alpha, number, scale }; diff --git a/node_modules/motion-dom/dist/es/value/types/numbers/units.mjs b/node_modules/motion-dom/dist/es/value/types/numbers/units.mjs new file mode 100644 index 00000000..858000e4 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/numbers/units.mjs @@ -0,0 +1,18 @@ +/*#__NO_SIDE_EFFECTS__*/ +const createUnitType = (unit) => ({ + test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1, + parse: parseFloat, + transform: (v) => `${v}${unit}`, +}); +const degrees = /*@__PURE__*/ createUnitType("deg"); +const percent = /*@__PURE__*/ createUnitType("%"); +const px = /*@__PURE__*/ createUnitType("px"); +const vh = /*@__PURE__*/ createUnitType("vh"); +const vw = /*@__PURE__*/ createUnitType("vw"); +const progressPercentage = /*@__PURE__*/ (() => ({ + ...percent, + parse: (v) => percent.parse(v) / 100, + transform: (v) => percent.transform(v * 100), +}))(); + +export { degrees, percent, progressPercentage, px, vh, vw }; diff --git a/node_modules/motion-dom/dist/es/value/types/test.mjs b/node_modules/motion-dom/dist/es/value/types/test.mjs new file mode 100644 index 00000000..ecc53d9e --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/test.mjs @@ -0,0 +1,6 @@ +/** + * Tests a provided value against a ValueType + */ +const testValueType = (v) => (type) => type.test(v); + +export { testValueType }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/animatable-none.mjs b/node_modules/motion-dom/dist/es/value/types/utils/animatable-none.mjs new file mode 100644 index 00000000..06d5a75a --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/animatable-none.mjs @@ -0,0 +1,15 @@ +import { complex } from '../complex/index.mjs'; +import { filter } from '../complex/filter.mjs'; +import { getDefaultValueType } from '../maps/defaults.mjs'; + +function getAnimatableNone(key, value) { + let defaultValueType = getDefaultValueType(key); + if (defaultValueType !== filter) + defaultValueType = complex; + // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target + return defaultValueType.getAnimatableNone + ? defaultValueType.getAnimatableNone(value) + : undefined; +} + +export { getAnimatableNone }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/color-regex.mjs b/node_modules/motion-dom/dist/es/value/types/utils/color-regex.mjs new file mode 100644 index 00000000..199e68b9 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/color-regex.mjs @@ -0,0 +1,3 @@ +const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu; + +export { colorRegex }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/find.mjs b/node_modules/motion-dom/dist/es/value/types/utils/find.mjs new file mode 100644 index 00000000..e0079667 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/find.mjs @@ -0,0 +1,15 @@ +import { color } from '../color/index.mjs'; +import { complex } from '../complex/index.mjs'; +import { dimensionValueTypes } from '../dimensions.mjs'; +import { testValueType } from '../test.mjs'; + +/** + * A list of all ValueTypes + */ +const valueTypes = [...dimensionValueTypes, color, complex]; +/** + * Tests a value against the list of ValueTypes + */ +const findValueType = (v) => valueTypes.find(testValueType(v)); + +export { findValueType }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/float-regex.mjs b/node_modules/motion-dom/dist/es/value/types/utils/float-regex.mjs new file mode 100644 index 00000000..9767bfc5 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/float-regex.mjs @@ -0,0 +1,3 @@ +const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu; + +export { floatRegex }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/get-as-type.mjs b/node_modules/motion-dom/dist/es/value/types/utils/get-as-type.mjs new file mode 100644 index 00000000..3376b80e --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/get-as-type.mjs @@ -0,0 +1,10 @@ +/** + * Provided a value and a ValueType, returns the value as that value type. + */ +const getValueAsType = (value, type) => { + return type && typeof value === "number" + ? type.transform(value) + : value; +}; + +export { getValueAsType }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/is-nullish.mjs b/node_modules/motion-dom/dist/es/value/types/utils/is-nullish.mjs new file mode 100644 index 00000000..23738a28 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/is-nullish.mjs @@ -0,0 +1,5 @@ +function isNullish(v) { + return v == null; +} + +export { isNullish }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/sanitize.mjs b/node_modules/motion-dom/dist/es/value/types/utils/sanitize.mjs new file mode 100644 index 00000000..846f9a6c --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/sanitize.mjs @@ -0,0 +1,5 @@ +// If this number is a decimal, make it just five decimal places +// to avoid exponents +const sanitize = (v) => Math.round(v * 100000) / 100000; + +export { sanitize }; diff --git a/node_modules/motion-dom/dist/es/value/types/utils/single-color-regex.mjs b/node_modules/motion-dom/dist/es/value/types/utils/single-color-regex.mjs new file mode 100644 index 00000000..76a68ab6 --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/types/utils/single-color-regex.mjs @@ -0,0 +1,3 @@ +const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu; + +export { singleColorRegex }; diff --git a/node_modules/motion-dom/dist/es/value/utils/is-motion-value.mjs b/node_modules/motion-dom/dist/es/value/utils/is-motion-value.mjs new file mode 100644 index 00000000..52046c8a --- /dev/null +++ b/node_modules/motion-dom/dist/es/value/utils/is-motion-value.mjs @@ -0,0 +1,3 @@ +const isMotionValue = (value) => Boolean(value && value.getVelocity); + +export { isMotionValue }; diff --git a/node_modules/motion-dom/dist/es/view/index.mjs b/node_modules/motion-dom/dist/es/view/index.mjs new file mode 100644 index 00000000..7b2ff558 --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/index.mjs @@ -0,0 +1,64 @@ +import { noop } from 'motion-utils'; +import { addToQueue } from './queue.mjs'; + +class ViewTransitionBuilder { + constructor(update, options = {}) { + this.currentSubject = "root"; + this.targets = new Map(); + this.notifyReady = noop; + this.readyPromise = new Promise((resolve) => { + this.notifyReady = resolve; + }); + this.update = update; + this.options = { + interrupt: "wait", + ...options, + }; + addToQueue(this); + } + get(subject) { + this.currentSubject = subject; + return this; + } + layout(keyframes, options) { + this.updateTarget("layout", keyframes, options); + return this; + } + new(keyframes, options) { + this.updateTarget("new", keyframes, options); + return this; + } + old(keyframes, options) { + this.updateTarget("old", keyframes, options); + return this; + } + enter(keyframes, options) { + this.updateTarget("enter", keyframes, options); + return this; + } + exit(keyframes, options) { + this.updateTarget("exit", keyframes, options); + return this; + } + crossfade(options) { + this.updateTarget("enter", { opacity: 1 }, options); + this.updateTarget("exit", { opacity: 0 }, options); + return this; + } + updateTarget(target, keyframes, options = {}) { + const { currentSubject, targets } = this; + if (!targets.has(currentSubject)) { + targets.set(currentSubject, {}); + } + const targetData = targets.get(currentSubject); + targetData[target] = { keyframes, options }; + } + then(resolve, reject) { + return this.readyPromise.then(resolve, reject); + } +} +function animateView(update, defaultOptions = {}) { + return new ViewTransitionBuilder(update, defaultOptions); +} + +export { ViewTransitionBuilder, animateView }; diff --git a/node_modules/motion-dom/dist/es/view/queue.mjs b/node_modules/motion-dom/dist/es/view/queue.mjs new file mode 100644 index 00000000..a6317528 --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/queue.mjs @@ -0,0 +1,52 @@ +import { removeItem } from 'motion-utils'; +import { microtask } from '../frameloop/microtask.mjs'; +import { startViewAnimation } from './start.mjs'; + +let builders = []; +let current = null; +function next() { + current = null; + const [nextBuilder] = builders; + if (nextBuilder) + start(nextBuilder); +} +function start(builder) { + removeItem(builders, builder); + current = builder; + startViewAnimation(builder).then((animation) => { + builder.notifyReady(animation); + animation.finished.finally(next); + }); +} +function processQueue() { + /** + * Iterate backwards over the builders array. We can ignore the + * "wait" animations. If we have an interrupting animation in the + * queue then we need to batch all preceeding animations into it. + * Currently this only batches the update functions but will also + * need to batch the targets. + */ + for (let i = builders.length - 1; i >= 0; i--) { + const builder = builders[i]; + const { interrupt } = builder.options; + if (interrupt === "immediate") { + const batchedUpdates = builders.slice(0, i + 1).map((b) => b.update); + const remaining = builders.slice(i + 1); + builder.update = () => { + batchedUpdates.forEach((update) => update()); + }; + // Put the current builder at the front, followed by any "wait" builders + builders = [builder, ...remaining]; + break; + } + } + if (!current || builders[0]?.options.interrupt === "immediate") { + next(); + } +} +function addToQueue(builder) { + builders.push(builder); + microtask.render(processQueue); +} + +export { addToQueue }; diff --git a/node_modules/motion-dom/dist/es/view/start.mjs b/node_modules/motion-dom/dist/es/view/start.mjs new file mode 100644 index 00000000..6cc71ad9 --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/start.mjs @@ -0,0 +1,155 @@ +import { secondsToMilliseconds } from 'motion-utils'; +import { GroupAnimation } from '../animation/GroupAnimation.mjs'; +import { NativeAnimation } from '../animation/NativeAnimation.mjs'; +import { NativeAnimationWrapper } from '../animation/NativeAnimationWrapper.mjs'; +import { getValueTransition } from '../animation/utils/get-value-transition.mjs'; +import { mapEasingToNativeEasing } from '../animation/waapi/easing/map-easing.mjs'; +import { applyGeneratorOptions } from '../animation/waapi/utils/apply-generator.mjs'; +import { chooseLayerType } from './utils/choose-layer-type.mjs'; +import { css } from './utils/css.mjs'; +import { getLayerName } from './utils/get-layer-name.mjs'; +import { getViewAnimations } from './utils/get-view-animations.mjs'; +import { hasTarget } from './utils/has-target.mjs'; + +const definitionNames = ["layout", "enter", "exit", "new", "old"]; +function startViewAnimation(builder) { + const { update, targets, options: defaultOptions } = builder; + if (!document.startViewTransition) { + return new Promise(async (resolve) => { + await update(); + resolve(new GroupAnimation([])); + }); + } + // TODO: Go over existing targets and ensure they all have ids + /** + * If we don't have any animations defined for the root target, + * remove it from being captured. + */ + if (!hasTarget("root", targets)) { + css.set(":root", { + "view-transition-name": "none", + }); + } + /** + * Set the timing curve to linear for all view transition layers. + * This gets baked into the keyframes, which can't be changed + * without breaking the generated animation. + * + * This allows us to set easing via updateTiming - which can be changed. + */ + css.set("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)", { "animation-timing-function": "linear !important" }); + css.commit(); // Write + const transition = document.startViewTransition(async () => { + await update(); + // TODO: Go over new targets and ensure they all have ids + }); + transition.finished.finally(() => { + css.remove(); // Write + }); + return new Promise((resolve) => { + transition.ready.then(() => { + const generatedViewAnimations = getViewAnimations(); + const animations = []; + /** + * Create animations for each of our explicitly-defined subjects. + */ + targets.forEach((definition, target) => { + // TODO: If target is not "root", resolve elements + // and iterate over each + for (const key of definitionNames) { + if (!definition[key]) + continue; + const { keyframes, options } = definition[key]; + for (let [valueName, valueKeyframes] of Object.entries(keyframes)) { + if (!valueKeyframes) + continue; + const valueOptions = { + ...getValueTransition(defaultOptions, valueName), + ...getValueTransition(options, valueName), + }; + const type = chooseLayerType(key); + /** + * If this is an opacity animation, and keyframes are not an array, + * we need to convert them into an array and set an initial value. + */ + if (valueName === "opacity" && + !Array.isArray(valueKeyframes)) { + const initialValue = type === "new" ? 0 : 1; + valueKeyframes = [initialValue, valueKeyframes]; + } + /** + * Resolve stagger function if provided. + */ + if (typeof valueOptions.delay === "function") { + valueOptions.delay = valueOptions.delay(0, 1); + } + valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay)); + const animation = new NativeAnimation({ + ...valueOptions, + element: document.documentElement, + name: valueName, + pseudoElement: `::view-transition-${type}(${target})`, + keyframes: valueKeyframes, + }); + animations.push(animation); + } + } + }); + /** + * Handle browser generated animations + */ + for (const animation of generatedViewAnimations) { + if (animation.playState === "finished") + continue; + const { effect } = animation; + if (!effect || !(effect instanceof KeyframeEffect)) + continue; + const { pseudoElement } = effect; + if (!pseudoElement) + continue; + const name = getLayerName(pseudoElement); + if (!name) + continue; + const targetDefinition = targets.get(name.layer); + if (!targetDefinition) { + /** + * If transition name is group then update the timing of the animation + * whereas if it's old or new then we could possibly replace it using + * the above method. + */ + const transitionName = name.type === "group" ? "layout" : ""; + let animationTransition = { + ...getValueTransition(defaultOptions, transitionName), + }; + animationTransition.duration && (animationTransition.duration = secondsToMilliseconds(animationTransition.duration)); + animationTransition = + applyGeneratorOptions(animationTransition); + const easing = mapEasingToNativeEasing(animationTransition.ease, animationTransition.duration); + effect.updateTiming({ + delay: secondsToMilliseconds(animationTransition.delay ?? 0), + duration: animationTransition.duration, + easing, + }); + animations.push(new NativeAnimationWrapper(animation)); + } + else if (hasOpacity(targetDefinition, "enter") && + hasOpacity(targetDefinition, "exit") && + effect + .getKeyframes() + .some((keyframe) => keyframe.mixBlendMode)) { + animations.push(new NativeAnimationWrapper(animation)); + } + else { + animation.cancel(); + } + } + resolve(new GroupAnimation(animations)); + }); + }); +} +function hasOpacity(target, key) { + return target?.[key]?.keyframes.opacity; +} + +export { startViewAnimation }; diff --git a/node_modules/motion-dom/dist/es/view/utils/choose-layer-type.mjs b/node_modules/motion-dom/dist/es/view/utils/choose-layer-type.mjs new file mode 100644 index 00000000..1e80123b --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/utils/choose-layer-type.mjs @@ -0,0 +1,11 @@ +function chooseLayerType(valueName) { + if (valueName === "layout") + return "group"; + if (valueName === "enter" || valueName === "new") + return "new"; + if (valueName === "exit" || valueName === "old") + return "old"; + return "group"; +} + +export { chooseLayerType }; diff --git a/node_modules/motion-dom/dist/es/view/utils/css.mjs b/node_modules/motion-dom/dist/es/view/utils/css.mjs new file mode 100644 index 00000000..b0ca1723 --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/utils/css.mjs @@ -0,0 +1,32 @@ +let pendingRules = {}; +let style = null; +const css = { + set: (selector, values) => { + pendingRules[selector] = values; + }, + commit: () => { + if (!style) { + style = document.createElement("style"); + style.id = "motion-view"; + } + let cssText = ""; + for (const selector in pendingRules) { + const rule = pendingRules[selector]; + cssText += `${selector} {\n`; + for (const [property, value] of Object.entries(rule)) { + cssText += ` ${property}: ${value};\n`; + } + cssText += "}\n"; + } + style.textContent = cssText; + document.head.appendChild(style); + pendingRules = {}; + }, + remove: () => { + if (style && style.parentElement) { + style.parentElement.removeChild(style); + } + }, +}; + +export { css }; diff --git a/node_modules/motion-dom/dist/es/view/utils/get-layer-name.mjs b/node_modules/motion-dom/dist/es/view/utils/get-layer-name.mjs new file mode 100644 index 00000000..f04d0f96 --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/utils/get-layer-name.mjs @@ -0,0 +1,8 @@ +function getLayerName(pseudoElement) { + const match = pseudoElement.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/); + if (!match) + return null; + return { layer: match[2], type: match[1] }; +} + +export { getLayerName }; diff --git a/node_modules/motion-dom/dist/es/view/utils/get-view-animations.mjs b/node_modules/motion-dom/dist/es/view/utils/get-view-animations.mjs new file mode 100644 index 00000000..9621dd32 --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/utils/get-view-animations.mjs @@ -0,0 +1,12 @@ +function filterViewAnimations(animation) { + const { effect } = animation; + if (!effect) + return false; + return (effect.target === document.documentElement && + effect.pseudoElement?.startsWith("::view-transition")); +} +function getViewAnimations() { + return document.getAnimations().filter(filterViewAnimations); +} + +export { getViewAnimations }; diff --git a/node_modules/motion-dom/dist/es/view/utils/has-target.mjs b/node_modules/motion-dom/dist/es/view/utils/has-target.mjs new file mode 100644 index 00000000..2516134a --- /dev/null +++ b/node_modules/motion-dom/dist/es/view/utils/has-target.mjs @@ -0,0 +1,5 @@ +function hasTarget(target, targets) { + return targets.has(target) && Object.keys(targets.get(target)).length > 0; +} + +export { hasTarget }; diff --git a/node_modules/motion-dom/dist/index.d.ts b/node_modules/motion-dom/dist/index.d.ts new file mode 100644 index 00000000..01dc3ced --- /dev/null +++ b/node_modules/motion-dom/dist/index.d.ts @@ -0,0 +1,3137 @@ +import { Box, Easing, EasingFunction, BezierDefinition, Point, BoundingBox, Axis } from 'motion-utils'; + +interface SVGAttributes { + accentHeight?: AnyResolvedKeyframe | undefined; + accumulate?: "none" | "sum" | undefined; + additive?: "replace" | "sum" | undefined; + alignmentBaseline?: "auto" | "baseline" | "before-edge" | "text-before-edge" | "middle" | "central" | "after-edge" | "text-after-edge" | "ideographic" | "alphabetic" | "hanging" | "mathematical" | "inherit" | undefined; + allowReorder?: "no" | "yes" | undefined; + alphabetic?: AnyResolvedKeyframe | undefined; + amplitude?: AnyResolvedKeyframe | undefined; + arabicForm?: "initial" | "medial" | "terminal" | "isolated" | undefined; + ascent?: AnyResolvedKeyframe | undefined; + attributeName?: string | undefined; + attributeType?: string | undefined; + autoReverse?: boolean | undefined; + azimuth?: AnyResolvedKeyframe | undefined; + baseFrequency?: AnyResolvedKeyframe | undefined; + baselineShift?: AnyResolvedKeyframe | undefined; + baseProfile?: AnyResolvedKeyframe | undefined; + bbox?: AnyResolvedKeyframe | undefined; + begin?: AnyResolvedKeyframe | undefined; + bias?: AnyResolvedKeyframe | undefined; + by?: AnyResolvedKeyframe | undefined; + calcMode?: AnyResolvedKeyframe | undefined; + capHeight?: AnyResolvedKeyframe | undefined; + clip?: AnyResolvedKeyframe | undefined; + clipPath?: string | undefined; + clipPathUnits?: AnyResolvedKeyframe | undefined; + clipRule?: AnyResolvedKeyframe | undefined; + colorInterpolation?: AnyResolvedKeyframe | undefined; + colorInterpolationFilters?: "auto" | "sRGB" | "linearRGB" | "inherit" | undefined; + colorProfile?: AnyResolvedKeyframe | undefined; + colorRendering?: AnyResolvedKeyframe | undefined; + contentScriptType?: AnyResolvedKeyframe | undefined; + contentStyleType?: AnyResolvedKeyframe | undefined; + cursor?: AnyResolvedKeyframe | undefined; + cx?: AnyResolvedKeyframe | undefined; + cy?: AnyResolvedKeyframe | undefined; + d?: string | undefined; + decelerate?: AnyResolvedKeyframe | undefined; + descent?: AnyResolvedKeyframe | undefined; + diffuseConstant?: AnyResolvedKeyframe | undefined; + direction?: AnyResolvedKeyframe | undefined; + display?: AnyResolvedKeyframe | undefined; + divisor?: AnyResolvedKeyframe | undefined; + dominantBaseline?: AnyResolvedKeyframe | undefined; + dur?: AnyResolvedKeyframe | undefined; + dx?: AnyResolvedKeyframe | undefined; + dy?: AnyResolvedKeyframe | undefined; + edgeMode?: AnyResolvedKeyframe | undefined; + elevation?: AnyResolvedKeyframe | undefined; + enableBackground?: AnyResolvedKeyframe | undefined; + end?: AnyResolvedKeyframe | undefined; + exponent?: AnyResolvedKeyframe | undefined; + externalResourcesRequired?: boolean | undefined; + fill?: string | undefined; + fillOpacity?: AnyResolvedKeyframe | undefined; + fillRule?: "nonzero" | "evenodd" | "inherit" | undefined; + filter?: string | undefined; + filterRes?: AnyResolvedKeyframe | undefined; + filterUnits?: AnyResolvedKeyframe | undefined; + floodColor?: AnyResolvedKeyframe | undefined; + floodOpacity?: AnyResolvedKeyframe | undefined; + focusable?: boolean | "auto" | undefined; + fontFamily?: string | undefined; + fontSize?: AnyResolvedKeyframe | undefined; + fontSizeAdjust?: AnyResolvedKeyframe | undefined; + fontStretch?: AnyResolvedKeyframe | undefined; + fontStyle?: AnyResolvedKeyframe | undefined; + fontVariant?: AnyResolvedKeyframe | undefined; + fontWeight?: AnyResolvedKeyframe | undefined; + format?: AnyResolvedKeyframe | undefined; + fr?: AnyResolvedKeyframe | undefined; + from?: AnyResolvedKeyframe | undefined; + fx?: AnyResolvedKeyframe | undefined; + fy?: AnyResolvedKeyframe | undefined; + g1?: AnyResolvedKeyframe | undefined; + g2?: AnyResolvedKeyframe | undefined; + glyphName?: AnyResolvedKeyframe | undefined; + glyphOrientationHorizontal?: AnyResolvedKeyframe | undefined; + glyphOrientationVertical?: AnyResolvedKeyframe | undefined; + glyphRef?: AnyResolvedKeyframe | undefined; + gradientTransform?: string | undefined; + gradientUnits?: string | undefined; + hanging?: AnyResolvedKeyframe | undefined; + horizAdvX?: AnyResolvedKeyframe | undefined; + horizOriginX?: AnyResolvedKeyframe | undefined; + href?: string | undefined; + ideographic?: AnyResolvedKeyframe | undefined; + imageRendering?: AnyResolvedKeyframe | undefined; + in2?: AnyResolvedKeyframe | undefined; + in?: string | undefined; + intercept?: AnyResolvedKeyframe | undefined; + k1?: AnyResolvedKeyframe | undefined; + k2?: AnyResolvedKeyframe | undefined; + k3?: AnyResolvedKeyframe | undefined; + k4?: AnyResolvedKeyframe | undefined; + k?: AnyResolvedKeyframe | undefined; + kernelMatrix?: AnyResolvedKeyframe | undefined; + kernelUnitLength?: AnyResolvedKeyframe | undefined; + kerning?: AnyResolvedKeyframe | undefined; + keyPoints?: AnyResolvedKeyframe | undefined; + keySplines?: AnyResolvedKeyframe | undefined; + keyTimes?: AnyResolvedKeyframe | undefined; + lengthAdjust?: AnyResolvedKeyframe | undefined; + letterSpacing?: AnyResolvedKeyframe | undefined; + lightingColor?: AnyResolvedKeyframe | undefined; + limitingConeAngle?: AnyResolvedKeyframe | undefined; + local?: AnyResolvedKeyframe | undefined; + markerEnd?: string | undefined; + markerHeight?: AnyResolvedKeyframe | undefined; + markerMid?: string | undefined; + markerStart?: string | undefined; + markerUnits?: AnyResolvedKeyframe | undefined; + markerWidth?: AnyResolvedKeyframe | undefined; + mask?: string | undefined; + maskContentUnits?: AnyResolvedKeyframe | undefined; + maskUnits?: AnyResolvedKeyframe | undefined; + mathematical?: AnyResolvedKeyframe | undefined; + mode?: AnyResolvedKeyframe | undefined; + numOctaves?: AnyResolvedKeyframe | undefined; + offset?: AnyResolvedKeyframe | undefined; + opacity?: AnyResolvedKeyframe | undefined; + operator?: AnyResolvedKeyframe | undefined; + order?: AnyResolvedKeyframe | undefined; + orient?: AnyResolvedKeyframe | undefined; + orientation?: AnyResolvedKeyframe | undefined; + origin?: AnyResolvedKeyframe | undefined; + overflow?: AnyResolvedKeyframe | undefined; + overlinePosition?: AnyResolvedKeyframe | undefined; + overlineThickness?: AnyResolvedKeyframe | undefined; + paintOrder?: AnyResolvedKeyframe | undefined; + panose1?: AnyResolvedKeyframe | undefined; + path?: string | undefined; + pathLength?: AnyResolvedKeyframe | undefined; + patternContentUnits?: string | undefined; + patternTransform?: AnyResolvedKeyframe | undefined; + patternUnits?: string | undefined; + pointerEvents?: AnyResolvedKeyframe | undefined; + points?: string | undefined; + pointsAtX?: AnyResolvedKeyframe | undefined; + pointsAtY?: AnyResolvedKeyframe | undefined; + pointsAtZ?: AnyResolvedKeyframe | undefined; + preserveAlpha?: boolean | undefined; + preserveAspectRatio?: string | undefined; + primitiveUnits?: AnyResolvedKeyframe | undefined; + r?: AnyResolvedKeyframe | undefined; + radius?: AnyResolvedKeyframe | undefined; + refX?: AnyResolvedKeyframe | undefined; + refY?: AnyResolvedKeyframe | undefined; + renderingIntent?: AnyResolvedKeyframe | undefined; + repeatCount?: AnyResolvedKeyframe | undefined; + repeatDur?: AnyResolvedKeyframe | undefined; + requiredExtensions?: AnyResolvedKeyframe | undefined; + requiredFeatures?: AnyResolvedKeyframe | undefined; + restart?: AnyResolvedKeyframe | undefined; + result?: string | undefined; + rotate?: AnyResolvedKeyframe | undefined; + rx?: AnyResolvedKeyframe | undefined; + ry?: AnyResolvedKeyframe | undefined; + scale?: AnyResolvedKeyframe | undefined; + seed?: AnyResolvedKeyframe | undefined; + shapeRendering?: AnyResolvedKeyframe | undefined; + slope?: AnyResolvedKeyframe | undefined; + spacing?: AnyResolvedKeyframe | undefined; + specularConstant?: AnyResolvedKeyframe | undefined; + specularExponent?: AnyResolvedKeyframe | undefined; + speed?: AnyResolvedKeyframe | undefined; + spreadMethod?: string | undefined; + startOffset?: AnyResolvedKeyframe | undefined; + stdDeviation?: AnyResolvedKeyframe | undefined; + stemh?: AnyResolvedKeyframe | undefined; + stemv?: AnyResolvedKeyframe | undefined; + stitchTiles?: AnyResolvedKeyframe | undefined; + stopColor?: string | undefined; + stopOpacity?: AnyResolvedKeyframe | undefined; + strikethroughPosition?: AnyResolvedKeyframe | undefined; + strikethroughThickness?: AnyResolvedKeyframe | undefined; + string?: AnyResolvedKeyframe | undefined; + stroke?: string | undefined; + strokeDasharray?: AnyResolvedKeyframe | undefined; + strokeDashoffset?: AnyResolvedKeyframe | undefined; + strokeLinecap?: "butt" | "round" | "square" | "inherit" | undefined; + strokeLinejoin?: "miter" | "round" | "bevel" | "inherit" | undefined; + strokeMiterlimit?: AnyResolvedKeyframe | undefined; + strokeOpacity?: AnyResolvedKeyframe | undefined; + strokeWidth?: AnyResolvedKeyframe | undefined; + surfaceScale?: AnyResolvedKeyframe | undefined; + systemLanguage?: AnyResolvedKeyframe | undefined; + tableValues?: AnyResolvedKeyframe | undefined; + targetX?: AnyResolvedKeyframe | undefined; + targetY?: AnyResolvedKeyframe | undefined; + textAnchor?: string | undefined; + textDecoration?: AnyResolvedKeyframe | undefined; + textLength?: AnyResolvedKeyframe | undefined; + textRendering?: AnyResolvedKeyframe | undefined; + to?: AnyResolvedKeyframe | undefined; + transform?: string | undefined; + u1?: AnyResolvedKeyframe | undefined; + u2?: AnyResolvedKeyframe | undefined; + underlinePosition?: AnyResolvedKeyframe | undefined; + underlineThickness?: AnyResolvedKeyframe | undefined; + unicode?: AnyResolvedKeyframe | undefined; + unicodeBidi?: AnyResolvedKeyframe | undefined; + unicodeRange?: AnyResolvedKeyframe | undefined; + unitsPerEm?: AnyResolvedKeyframe | undefined; + vAlphabetic?: AnyResolvedKeyframe | undefined; + values?: string | undefined; + vectorEffect?: AnyResolvedKeyframe | undefined; + version?: string | undefined; + vertAdvY?: AnyResolvedKeyframe | undefined; + vertOriginX?: AnyResolvedKeyframe | undefined; + vertOriginY?: AnyResolvedKeyframe | undefined; + vHanging?: AnyResolvedKeyframe | undefined; + vIdeographic?: AnyResolvedKeyframe | undefined; + viewBox?: string | undefined; + viewTarget?: AnyResolvedKeyframe | undefined; + visibility?: AnyResolvedKeyframe | undefined; + vMathematical?: AnyResolvedKeyframe | undefined; + widths?: AnyResolvedKeyframe | undefined; + wordSpacing?: AnyResolvedKeyframe | undefined; + writingMode?: AnyResolvedKeyframe | undefined; + x1?: AnyResolvedKeyframe | undefined; + x2?: AnyResolvedKeyframe | undefined; + x?: AnyResolvedKeyframe | undefined; + xChannelSelector?: string | undefined; + xHeight?: AnyResolvedKeyframe | undefined; + xlinkActuate?: string | undefined; + xlinkArcrole?: string | undefined; + xlinkHref?: string | undefined; + xlinkRole?: string | undefined; + xlinkShow?: string | undefined; + xlinkTitle?: string | undefined; + xlinkType?: string | undefined; + xmlBase?: string | undefined; + xmlLang?: string | undefined; + xmlns?: string | undefined; + xmlnsXlink?: string | undefined; + xmlSpace?: string | undefined; + y1?: AnyResolvedKeyframe | undefined; + y2?: AnyResolvedKeyframe | undefined; + y?: AnyResolvedKeyframe | undefined; + yChannelSelector?: string | undefined; + z?: AnyResolvedKeyframe | undefined; + zoomAndPan?: string | undefined; +} + +/** + * An update function. It accepts a timestamp used to advance the animation. + */ +type Update$1 = (timestamp: number) => void; +/** + * Drivers accept a update function and call it at an interval. This interval + * could be a synchronous loop, a setInterval, or tied to the device's framerate. + */ +interface DriverControls { + start: (keepAlive?: boolean) => void; + stop: () => void; + now: () => number; +} +type Driver = (update: Update$1) => DriverControls; + +/** + * Temporary subset of VisualElement until VisualElement is + * moved to motion-dom + */ +interface WithRender { + render: () => void; + readValue: (name: string, keyframe: any) => any; + getValue: (name: string, defaultValue?: any) => any; + current?: HTMLElement | SVGElement; + measureViewportBox: () => Box; +} + +type AnyResolvedKeyframe = string | number; +interface ProgressTimeline { + currentTime: null | { + value: number; + }; + cancel?: VoidFunction; +} +interface ValueAnimationOptionsWithRenderContext extends ValueAnimationOptions { + KeyframeResolver?: typeof KeyframeResolver; + motionValue?: MotionValue; + element?: WithRender; +} +interface TimelineWithFallback { + timeline?: ProgressTimeline; + observe: (animation: AnimationPlaybackControls) => VoidFunction; +} +/** + * Methods to control an animation. + */ +interface AnimationPlaybackControls { + /** + * The current time of the animation, in seconds. + */ + time: number; + /** + * The playback speed of the animation. + * 1 = normal speed, 2 = double speed, 0.5 = half speed. + */ + speed: number; + /** + * The start time of the animation, in milliseconds. + */ + startTime: number | null; + /** + * The state of the animation. + * + * This is currently for internal use only. + */ + state: AnimationPlayState; + duration: number; + /** + * Stops the animation at its current state, and prevents it from + * resuming when the animation is played again. + */ + stop: () => void; + /** + * Plays the animation. + */ + play: () => void; + /** + * Pauses the animation. + */ + pause: () => void; + /** + * Completes the animation and applies the final state. + */ + complete: () => void; + /** + * Cancels the animation and applies the initial state. + */ + cancel: () => void; + /** + * Attaches a timeline to the animation, for instance the `ScrollTimeline`. + * + * This is currently for internal use only. + */ + attachTimeline: (timeline: TimelineWithFallback) => VoidFunction; + finished: Promise; +} +type AnimationPlaybackControlsWithThen = AnimationPlaybackControls & { + then: (onResolve: VoidFunction, onReject?: VoidFunction) => Promise; +}; +interface AnimationState { + value: V; + done: boolean; +} +interface KeyframeGenerator { + calculatedDuration: null | number; + next: (t: number) => AnimationState; + toString: () => string; +} +interface DOMValueAnimationOptions extends ValueAnimationTransition { + element: HTMLElement | SVGElement; + keyframes: ValueKeyframesDefinition; + name: string; + pseudoElement?: string; + allowFlatten?: boolean; +} +interface ValueAnimationOptions extends ValueAnimationTransition { + keyframes: V[]; + element?: any; + name?: string; + motionValue?: MotionValue; + from?: any; + isHandoff?: boolean; + allowFlatten?: boolean; + finalKeyframe?: V; +} +type GeneratorFactoryFunction = (options: ValueAnimationOptions) => KeyframeGenerator; +interface GeneratorFactory extends GeneratorFactoryFunction { + applyToOptions?: (options: Transition) => Transition; +} +type AnimationGeneratorName = "decay" | "spring" | "keyframes" | "tween" | "inertia"; +type AnimationGeneratorType = GeneratorFactory | AnimationGeneratorName | false; +interface AnimationPlaybackLifecycles { + onUpdate?: (latest: V) => void; + onPlay?: () => void; + onComplete?: () => void; + onRepeat?: () => void; + onStop?: () => void; +} +interface ValueAnimationTransition extends ValueTransition, AnimationPlaybackLifecycles { + isSync?: boolean; +} +type RepeatType = "loop" | "reverse" | "mirror"; +interface AnimationPlaybackOptions { + /** + * The number of times to repeat the transition. Set to `Infinity` for perpetual repeating. + * + * Without setting `repeatType`, this will loop the animation. + * + * @public + */ + repeat?: number; + /** + * How to repeat the animation. This can be either: + * + * "loop": Repeats the animation from the start + * + * "reverse": Alternates between forward and backwards playback + * + * "mirror": Switches `from` and `to` alternately + * + * @public + */ + repeatType?: RepeatType; + /** + * When repeating an animation, `repeatDelay` will set the + * duration of the time to wait, in seconds, between each repetition. + * + * @public + */ + repeatDelay?: number; +} +interface VelocityOptions { + velocity?: number; + /** + * End animation if absolute speed (in units per second) drops below this + * value and delta is smaller than `restDelta`. Set to `0.01` by default. + * + * @public + */ + restSpeed?: number; + /** + * End animation if distance is below this value and speed is below + * `restSpeed`. When animation ends, spring gets "snapped" to. Set to + * `0.01` by default. + * + * @public + */ + restDelta?: number; +} +interface DurationSpringOptions { + /** + * The total duration of the animation. Set to `0.3` by default. + * + * @public + */ + duration?: number; + /** + * If visualDuration is set, this will override duration. + * + * The visual duration is a time, set in seconds, that the animation will take to visually appear to reach its target. + * + * In other words, the bulk of the transition will occur before this time, and the "bouncy bit" will mostly happen after. + * + * This makes it easier to edit a spring, as well as visually coordinate it with other time-based animations. + * + * @public + */ + visualDuration?: number; + /** + * `bounce` determines the "bounciness" of a spring animation. + * + * `0` is no bounce, and `1` is extremely bouncy. + * + * If `duration` is set, this defaults to `0.25`. + * + * Note: `bounce` and `duration` will be overridden if `stiffness`, `damping` or `mass` are set. + * + * @public + */ + bounce?: number; +} +interface SpringOptions extends DurationSpringOptions, VelocityOptions { + /** + * Stiffness of the spring. Higher values will create more sudden movement. + * Set to `100` by default. + * + * @public + */ + stiffness?: number; + /** + * Strength of opposing force. If set to 0, spring will oscillate + * indefinitely. Set to `10` by default. + * + * @public + */ + damping?: number; + /** + * Mass of the moving object. Higher values will result in more lethargic + * movement. Set to `1` by default. + * + * @public + */ + mass?: number; +} +/** + * @deprecated Use SpringOptions instead + */ +interface Spring extends SpringOptions { +} +interface DecayOptions extends VelocityOptions { + keyframes?: number[]; + /** + * A higher power value equals a further target. Set to `0.8` by default. + * + * @public + */ + power?: number; + /** + * Adjusting the time constant will change the duration of the + * deceleration, thereby affecting its feel. Set to `700` by default. + * + * @public + */ + timeConstant?: number; + /** + * A function that receives the automatically-calculated target and returns a new one. Useful for snapping the target to a grid. + * + * @public + */ + modifyTarget?: (v: number) => number; +} +interface InertiaOptions extends DecayOptions { + /** + * If `min` or `max` is set, this affects the stiffness of the bounce + * spring. Higher values will create more sudden movement. Set to `500` by + * default. + * + * @public + */ + bounceStiffness?: number; + /** + * If `min` or `max` is set, this affects the damping of the bounce spring. + * If set to `0`, spring will oscillate indefinitely. Set to `10` by + * default. + * + * @public + */ + bounceDamping?: number; + /** + * Minimum constraint. If set, the value will "bump" against this value (or immediately spring to it if the animation starts as less than this value). + * + * @public + */ + min?: number; + /** + * Maximum constraint. If set, the value will "bump" against this value (or immediately snap to it, if the initial animation value exceeds this value). + * + * @public + */ + max?: number; +} +interface AnimationOrchestrationOptions { + /** + * Delay the animation by this duration (in seconds). Defaults to `0`. + * + * @public + */ + delay?: number; + /** + * Describes the relationship between the transition and its children. Set + * to `false` by default. + * + * @remarks + * When using variants, the transition can be scheduled in relation to its + * children with either `"beforeChildren"` to finish this transition before + * starting children transitions, `"afterChildren"` to finish children + * transitions before starting this transition. + * + * @public + */ + when?: false | "beforeChildren" | "afterChildren" | string; + /** + * When using variants, children animations will start after this duration + * (in seconds). You can add the `transition` property to both the `motion.div` and the + * `variant` directly. Adding it to the `variant` generally offers more flexibility, + * as it allows you to customize the delay per visual state. + * + * @public + */ + delayChildren?: number | DynamicOption; + /** + * When using variants, animations of child components can be staggered by this + * duration (in seconds). + * + * For instance, if `staggerChildren` is `0.01`, the first child will be + * delayed by `0` seconds, the second by `0.01`, the third by `0.02` and so + * on. + * + * The calculated stagger delay will be added to `delayChildren`. + * + * @deprecated - Use `delayChildren: stagger(interval)` instead. + */ + staggerChildren?: number; + /** + * The direction in which to stagger children. + * + * A value of `1` staggers from the first to the last while `-1` + * staggers from the last to the first. + * + * @deprecated - Use `delayChildren: stagger(interval, { from: "last" })` instead. + */ + staggerDirection?: number; +} +interface KeyframeOptions { + /** + * The total duration of the animation. Set to `0.3` by default. + * + * @public + */ + duration?: number; + ease?: Easing | Easing[]; + times?: number[]; +} +interface ValueTransition extends AnimationOrchestrationOptions, AnimationPlaybackOptions, Omit, Omit, KeyframeOptions { + /** + * Delay the animation by this duration (in seconds). Defaults to `0`. + * + * @public + */ + delay?: number; + /** + * The duration of time already elapsed in the animation. Set to `0` by + * default. + */ + elapsed?: number; + driver?: Driver; + /** + * Type of animation to use. + * + * - "tween": Duration-based animation with ease curve + * - "spring": Physics or duration-based spring animation + * - false: Use an instant animation + */ + type?: AnimationGeneratorType; + /** + * The duration of the tween animation. Set to `0.3` by default, 0r `0.8` if animating a series of keyframes. + * + * @public + */ + duration?: number; + autoplay?: boolean; + startTime?: number; + from?: any; +} +/** + * @deprecated Use KeyframeOptions instead + */ +interface Tween extends KeyframeOptions { +} +type SVGForcedAttrTransitions = { + [K in keyof SVGForcedAttrProperties]: ValueTransition; +}; +type SVGPathTransitions = { + [K in keyof SVGPathProperties]: ValueTransition; +}; +type SVGTransitions = { + [K in keyof Omit]: ValueTransition; +}; +interface VariableTransitions { + [key: `--${string}`]: ValueTransition; +} +type StyleTransitions = { + [K in keyof CSSStyleDeclarationWithTransform]?: ValueTransition; +}; +type ValueKeyframe = T; +type UnresolvedValueKeyframe = ValueKeyframe | null; +type ResolvedValueKeyframe = ValueKeyframe | ValueKeyframe[]; +type ValueKeyframesDefinition = ValueKeyframe | ValueKeyframe[] | UnresolvedValueKeyframe[]; +type StyleKeyframesDefinition = { + [K in keyof CSSStyleDeclarationWithTransform]?: ValueKeyframesDefinition; +}; +type SVGKeyframesDefinition = { + [K in keyof Omit]?: ValueKeyframesDefinition; +}; +interface VariableKeyframesDefinition { + [key: `--${string}`]: ValueKeyframesDefinition; +} +type SVGForcedAttrKeyframesDefinition = { + [K in keyof SVGForcedAttrProperties]?: ValueKeyframesDefinition; +}; +type SVGPathKeyframesDefinition = { + [K in keyof SVGPathProperties]?: ValueKeyframesDefinition; +}; +type DOMKeyframesDefinition = StyleKeyframesDefinition & SVGKeyframesDefinition & SVGPathKeyframesDefinition & SVGForcedAttrKeyframesDefinition & VariableKeyframesDefinition; +interface Target extends DOMKeyframesDefinition { +} +type CSSPropertyKeys = { + [K in keyof CSSStyleDeclaration as K extends string ? CSSStyleDeclaration[K] extends AnyResolvedKeyframe ? K : never : never]: CSSStyleDeclaration[K]; +}; +interface CSSStyleDeclarationWithTransform extends Omit { + x: number | string; + y: number | string; + z: number | string; + originX: number; + originY: number; + originZ: number; + translateX: number | string; + translateY: number | string; + translateZ: number | string; + rotateX: number | string; + rotateY: number | string; + rotateZ: number | string; + scaleX: number; + scaleY: number; + scaleZ: number; + skewX: number | string; + skewY: number | string; + transformPerspective: number; +} +type TransitionWithValueOverrides = ValueAnimationTransition & StyleTransitions & SVGPathTransitions & SVGForcedAttrTransitions & SVGTransitions & VariableTransitions & { + default?: ValueTransition; + layout?: ValueTransition; +}; +type Transition = ValueAnimationTransition | TransitionWithValueOverrides; +type DynamicOption = (i: number, total: number) => T; +type ValueAnimationWithDynamicDelay = Omit, "delay"> & { + delay?: number | DynamicOption; +}; +type AnimationOptions = ValueAnimationWithDynamicDelay | (ValueAnimationWithDynamicDelay & StyleTransitions & SVGPathTransitions & SVGForcedAttrTransitions & SVGTransitions & VariableTransitions & { + default?: ValueTransition; + layout?: ValueTransition; +}); +interface TransformProperties { + x?: AnyResolvedKeyframe; + y?: AnyResolvedKeyframe; + z?: AnyResolvedKeyframe; + translateX?: AnyResolvedKeyframe; + translateY?: AnyResolvedKeyframe; + translateZ?: AnyResolvedKeyframe; + rotate?: AnyResolvedKeyframe; + rotateX?: AnyResolvedKeyframe; + rotateY?: AnyResolvedKeyframe; + rotateZ?: AnyResolvedKeyframe; + scale?: AnyResolvedKeyframe; + scaleX?: AnyResolvedKeyframe; + scaleY?: AnyResolvedKeyframe; + scaleZ?: AnyResolvedKeyframe; + skew?: AnyResolvedKeyframe; + skewX?: AnyResolvedKeyframe; + skewY?: AnyResolvedKeyframe; + originX?: AnyResolvedKeyframe; + originY?: AnyResolvedKeyframe; + originZ?: AnyResolvedKeyframe; + perspective?: AnyResolvedKeyframe; + transformPerspective?: AnyResolvedKeyframe; +} +interface SVGForcedAttrProperties { + attrX?: number; + attrY?: number; + attrScale?: number; +} +interface SVGPathProperties { + pathLength?: number; + pathOffset?: number; + pathSpacing?: number; +} + +/** + * @public + */ +type Subscriber = (v: T) => void; +/** + * @public + */ +type PassiveEffect = (v: T, safeSetter: (v: T) => void) => void; +type StartAnimation = (complete: () => void) => AnimationPlaybackControlsWithThen | undefined; +interface MotionValueEventCallbacks { + animationStart: () => void; + animationComplete: () => void; + animationCancel: () => void; + change: (latestValue: V) => void; + renderRequest: () => void; + destroy: () => void; +} +interface ResolvedValues$1 { + [key: string]: AnyResolvedKeyframe; +} +interface Owner { + current: HTMLElement | unknown; + getProps: () => { + onUpdate?: (latest: ResolvedValues$1) => void; + transformTemplate?: (transform: TransformProperties, generatedTransform: string) => string; + }; +} +interface MotionValueOptions { + owner?: Owner; +} +declare const collectMotionValues: { + current: MotionValue[] | undefined; +}; +/** + * `MotionValue` is used to track the state and velocity of motion values. + * + * @public + */ +declare class MotionValue { + /** + * If a MotionValue has an owner, it was created internally within Motion + * and therefore has no external listeners. It is therefore safe to animate via WAAPI. + */ + owner?: Owner; + /** + * The current state of the `MotionValue`. + */ + private current; + /** + * The previous state of the `MotionValue`. + */ + private prev; + /** + * The previous state of the `MotionValue` at the end of the previous frame. + */ + private prevFrameValue; + /** + * The last time the `MotionValue` was updated. + */ + updatedAt: number; + /** + * The time `prevFrameValue` was updated. + */ + prevUpdatedAt: number | undefined; + private stopPassiveEffect?; + /** + * Whether the passive effect is active. + */ + isEffectActive?: boolean; + /** + * A reference to the currently-controlling animation. + */ + animation?: AnimationPlaybackControlsWithThen; + /** + * A list of MotionValues whose values are computed from this one. + * This is a rough start to a proper signal-like dirtying system. + */ + private dependents; + /** + * Tracks whether this value should be removed + */ + liveStyle?: boolean; + /** + * @param init - The initiating value + * @param config - Optional configuration options + * + * - `transformer`: A function to transform incoming values with. + */ + constructor(init: V, options?: MotionValueOptions); + setCurrent(current: V): void; + setPrevFrameValue(prevFrameValue?: V | undefined): void; + /** + * Adds a function that will be notified when the `MotionValue` is updated. + * + * It returns a function that, when called, will cancel the subscription. + * + * When calling `onChange` inside a React component, it should be wrapped with the + * `useEffect` hook. As it returns an unsubscribe function, this should be returned + * from the `useEffect` function to ensure you don't add duplicate subscribers.. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const y = useMotionValue(0) + * const opacity = useMotionValue(1) + * + * useEffect(() => { + * function updateOpacity() { + * const maxXY = Math.max(x.get(), y.get()) + * const newOpacity = transform(maxXY, [0, 100], [1, 0]) + * opacity.set(newOpacity) + * } + * + * const unsubscribeX = x.on("change", updateOpacity) + * const unsubscribeY = y.on("change", updateOpacity) + * + * return () => { + * unsubscribeX() + * unsubscribeY() + * } + * }, []) + * + * return + * } + * ``` + * + * @param subscriber - A function that receives the latest value. + * @returns A function that, when called, will cancel this subscription. + * + * @deprecated + */ + onChange(subscription: Subscriber): () => void; + /** + * An object containing a SubscriptionManager for each active event. + */ + private events; + on>(eventName: EventName, callback: MotionValueEventCallbacks[EventName]): VoidFunction; + clearListeners(): void; + /** + * Attaches a passive effect to the `MotionValue`. + */ + attach(passiveEffect: PassiveEffect, stopPassiveEffect: VoidFunction): void; + /** + * Sets the state of the `MotionValue`. + * + * @remarks + * + * ```jsx + * const x = useMotionValue(0) + * x.set(10) + * ``` + * + * @param latest - Latest value to set. + * @param render - Whether to notify render subscribers. Defaults to `true` + * + * @public + */ + set(v: V, render?: boolean): void; + setWithVelocity(prev: V, current: V, delta: number): void; + /** + * Set the state of the `MotionValue`, stopping any active animations, + * effects, and resets velocity to `0`. + */ + jump(v: V, endAnimation?: boolean): void; + dirty(): void; + addDependent(dependent: MotionValue): void; + removeDependent(dependent: MotionValue): void; + updateAndNotify: (v: V, render?: boolean) => void; + /** + * Returns the latest state of `MotionValue` + * + * @returns - The latest state of `MotionValue` + * + * @public + */ + get(): NonNullable; + /** + * @public + */ + getPrevious(): V | undefined; + /** + * Returns the latest velocity of `MotionValue` + * + * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. + * + * @public + */ + getVelocity(): number; + hasAnimated: boolean; + /** + * Registers a new animation to control this `MotionValue`. Only one + * animation can drive a `MotionValue` at one time. + * + * ```jsx + * value.start() + * ``` + * + * @param animation - A function that starts the provided animation + */ + start(startAnimation: StartAnimation): Promise; + /** + * Stop the currently active animation. + * + * @public + */ + stop(): void; + /** + * Returns `true` if this value is currently animating. + * + * @public + */ + isAnimating(): boolean; + private clearAnimation; + /** + * Destroy and clean up subscribers to this `MotionValue`. + * + * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically + * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually + * created a `MotionValue` via the `motionValue` function. + * + * @public + */ + destroy(): void; +} +declare function motionValue(init: V, options?: MotionValueOptions): MotionValue; + +type UnresolvedKeyframes = Array; +type ResolvedKeyframes = Array; +declare function flushKeyframeResolvers(): void; +type OnKeyframesResolved = (resolvedKeyframes: ResolvedKeyframes, finalKeyframe: T, forced: boolean) => void; +declare class KeyframeResolver { + name?: string; + element?: WithRender; + finalKeyframe?: T; + suspendedScrollY?: number; + protected unresolvedKeyframes: UnresolvedKeyframes; + private motionValue?; + private onComplete; + state: "pending" | "scheduled" | "complete"; + /** + * Track whether this resolver is async. If it is, it'll be added to the + * resolver queue and flushed in the next frame. Resolvers that aren't going + * to trigger read/write thrashing don't need to be async. + */ + private isAsync; + /** + * Track whether this resolver needs to perform a measurement + * to resolve its keyframes. + */ + needsMeasurement: boolean; + constructor(unresolvedKeyframes: UnresolvedKeyframes, onComplete: OnKeyframesResolved, name?: string, motionValue?: MotionValue, element?: WithRender, isAsync?: boolean); + scheduleResolve(): void; + readKeyframes(): void; + setFinalKeyframe(): void; + measureInitialState(): void; + renderEndStyles(): void; + measureEndState(): void; + complete(isForcedComplete?: boolean): void; + cancel(): void; + resume(): void; +} + +declare class WithPromise { + protected _finished: Promise; + resolve: VoidFunction; + constructor(); + get finished(): Promise; + protected updateFinished(): void; + protected notifyFinished(): void; + /** + * Allows the animation to be awaited. + * + * @deprecated Use `finished` instead. + */ + then(onResolve: VoidFunction, onReject?: VoidFunction): Promise; +} + +type OptionsWithoutKeyframes = Omit, "keyframes">; +declare class AsyncMotionValueAnimation extends WithPromise implements AnimationPlaybackControls { + private createdAt; + private resolvedAt; + private _animation; + private pendingTimeline; + private keyframeResolver; + private stopTimeline; + constructor({ autoplay, delay, type, repeat, repeatDelay, repeatType, keyframes, name, motionValue, element, ...options }: ValueAnimationOptions); + onKeyframesResolved(keyframes: ResolvedKeyframes, finalKeyframe: T, options: OptionsWithoutKeyframes, sync: boolean): void; + get finished(): Promise; + then(onResolve: VoidFunction, _onReject?: VoidFunction): Promise; + get animation(): AnimationPlaybackControls; + get duration(): number; + get time(): number; + set time(newTime: number); + get speed(): number; + get state(): AnimationPlayState; + set speed(newSpeed: number); + get startTime(): number | null; + attachTimeline(timeline: TimelineWithFallback): () => void; + play(): void; + pause(): void; + complete(): void; + cancel(): void; + /** + * Bound to support return animation.stop pattern + */ + stop: () => void; +} + +type AcceptedAnimations = AnimationPlaybackControls; +type GroupedAnimations = AcceptedAnimations[]; +declare class GroupAnimation implements AnimationPlaybackControls { + animations: GroupedAnimations; + constructor(animations: Array); + get finished(): Promise; + /** + * TODO: Filter out cancelled or stopped animations before returning + */ + private getAll; + private setAll; + attachTimeline(timeline: TimelineWithFallback): () => void; + get time(): number; + set time(time: number); + get speed(): number; + set speed(speed: number); + get state(): any; + get startTime(): any; + get duration(): number; + private runAll; + play(): void; + pause(): void; + stop: () => void; + cancel(): void; + complete(): void; +} + +declare class GroupAnimationWithThen extends GroupAnimation implements AnimationPlaybackControlsWithThen { + then(onResolve: VoidFunction, _onReject?: VoidFunction): Promise; +} + +declare class JSAnimation extends WithPromise implements AnimationPlaybackControlsWithThen { + state: AnimationPlayState; + startTime: number | null; + /** + * The driver that's controlling the animation loop. Normally this is a requestAnimationFrame loop + * but in tests we can pass in a synchronous loop. + */ + private driver?; + private isStopped; + private generator; + private calculatedDuration; + private resolvedDuration; + private totalDuration; + private options; + /** + * The current time of the animation. + */ + private currentTime; + /** + * The time at which the animation was paused. + */ + private holdTime; + /** + * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed. + */ + private playbackSpeed; + private mixKeyframes; + private mirroredGenerator; + constructor(options: ValueAnimationOptions); + initAnimation(): void; + updateTime(timestamp: number): void; + tick(timestamp: number, sample?: boolean): AnimationState; + /** + * Allows the returned animation to be awaited or promise-chained. Currently + * resolves when the animation finishes at all but in a future update could/should + * reject if its cancels. + */ + then(resolve: VoidFunction, reject?: VoidFunction): Promise; + get duration(): number; + get time(): number; + set time(newTime: number); + get speed(): number; + set speed(newSpeed: number); + play(): void; + pause(): void; + /** + * This method is bound to the instance to fix a pattern where + * animation.stop is returned as a reference from a useEffect. + */ + stop: () => void; + complete(): void; + finish(): void; + cancel(): void; + private teardown; + private stopDriver; + sample(sampleTime: number): AnimationState; + attachTimeline(timeline: TimelineWithFallback): VoidFunction; +} +declare function animateValue(options: ValueAnimationOptions): JSAnimation; + +interface NativeAnimationOptions extends DOMValueAnimationOptions { + pseudoElement?: string; + startTime?: number; +} +/** + * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API. + */ +declare class NativeAnimation extends WithPromise implements AnimationPlaybackControlsWithThen { + /** + * The interfaced Web Animation API animation + */ + protected animation: Animation; + protected finishedTime: number | null; + protected options: NativeAnimationOptions; + private allowFlatten; + private isStopped; + private isPseudoElement; + constructor(options?: NativeAnimationOptions); + updateMotionValue?(value?: T): void; + play(): void; + pause(): void; + complete(): void; + cancel(): void; + stop(): void; + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * In this method, we commit styles back to the DOM before cancelling + * the animation. + * + * This is designed to be overridden by NativeAnimationExtended, which + * will create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to also correctly calculate velocity for any subsequent animation + * while deferring the commit until the next animation frame. + */ + protected commitStyles(): void; + get duration(): number; + get time(): number; + set time(newTime: number); + /** + * The playback speed of the animation. + * 1 = normal speed, 2 = double speed, 0.5 = half speed. + */ + get speed(): number; + set speed(newSpeed: number); + get state(): AnimationPlayState; + get startTime(): number; + set startTime(newStartTime: number); + /** + * Attaches a timeline to the animation, for instance the `ScrollTimeline`. + */ + attachTimeline({ timeline, observe }: TimelineWithFallback): VoidFunction; +} + +type NativeAnimationOptionsExtended = NativeAnimationOptions & ValueAnimationOptions & NativeAnimationOptions; +declare class NativeAnimationExtended extends NativeAnimation { + options: NativeAnimationOptionsExtended; + constructor(options: NativeAnimationOptionsExtended); + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * Rather than read commited styles back out of the DOM, we can + * create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to calculate velocity for any subsequent animation. + */ + updateMotionValue(value?: T): void; +} + +declare class NativeAnimationWrapper extends NativeAnimation { + constructor(animation: Animation); +} + +declare const animationMapKey: (name: string, pseudoElement?: string) => string; +declare function getAnimationMap(element: Element): Map; + +type CSSVariableName = `--${string}`; +type CSSVariableToken = `var(${CSSVariableName})`; +declare const isCSSVariableName: (key?: AnyResolvedKeyframe | null) => key is `--${string}`; +declare const isCSSVariableToken: (value?: string) => value is `var(--${string})`; + +declare function parseCSSVariable(current: string): string[] | undefined[]; +declare function getVariableValue(current: CSSVariableToken, element: Element, depth?: number): AnyResolvedKeyframe | undefined; + +declare function getValueTransition(transition: any, key: string): any; + +declare function inertia({ keyframes, velocity, power, timeConstant, bounceDamping, bounceStiffness, modifyTarget, min, max, restDelta, restSpeed, }: ValueAnimationOptions): KeyframeGenerator; + +declare function defaultEasing(values: any[], easing?: EasingFunction): EasingFunction[]; +declare function keyframes({ duration, keyframes: keyframeValues, times, ease, }: ValueAnimationOptions): KeyframeGenerator; + +declare function spring(optionsOrVisualDuration?: ValueAnimationOptions | number, bounce?: number): KeyframeGenerator; +declare namespace spring { + var applyToOptions: (options: Transition) => Transition; +} + +/** + * Implement a practical max duration for keyframe generation + * to prevent infinite loops + */ +declare const maxGeneratorDuration = 20000; +declare function calcGeneratorDuration(generator: KeyframeGenerator): number; + +/** + * Create a progress => progress easing function from a generator. + */ +declare function createGeneratorEasing(options: Transition, scale: number | undefined, createGenerator: GeneratorFactory): { + type: string; + ease: (progress: number) => number; + duration: number; +}; + +declare function isGenerator(type?: AnimationGeneratorType): type is GeneratorFactory; + +declare class DOMKeyframesResolver extends KeyframeResolver { + name: string; + element?: WithRender; + private removedTransforms?; + private measuredOrigin?; + constructor(unresolvedKeyframes: UnresolvedKeyframes, onComplete: OnKeyframesResolved, name?: string, motionValue?: MotionValue, element?: WithRender); + readKeyframes(): void; + resolveNoneKeyframes(): void; + measureInitialState(): void; + measureEndState(): void; +} + +declare function defaultOffset(arr: any[]): number[]; + +declare function fillOffset(offset: number[], remaining: number): void; + +declare function convertOffsetToTimes(offset: number[], duration: number): number[]; + +declare function applyPxDefaults(keyframes: ValueKeyframe[] | UnresolvedValueKeyframe[], name: string): void; + +declare function fillWildcards(keyframes: ValueKeyframe[] | UnresolvedValueKeyframe[]): void; + +declare const cubicBezierAsString: ([a, b, c, d]: BezierDefinition) => string; + +declare function isWaapiSupportedEasing(easing?: Easing | Easing[]): boolean; + +declare function mapEasingToNativeEasing(easing: Easing | Easing[] | undefined, duration: number): undefined | string | string[]; + +declare const supportedWaapiEasing: { + linear: string; + ease: string; + easeIn: string; + easeOut: string; + easeInOut: string; + circIn: string; + circOut: string; + backIn: string; + backOut: string; +}; + +declare function startWaapiAnimation(element: Element, valueName: string, keyframes: ValueKeyframesDefinition, { delay, duration, repeat, repeatType, ease, times, }?: ValueTransition, pseudoElement?: string | undefined): Animation; + +declare const supportsPartialKeyframes: () => boolean; + +declare function supportsBrowserAnimation(options: ValueAnimationOptionsWithRenderContext): any; + +/** + * A list of values that can be hardware-accelerated. + */ +declare const acceleratedValues: Set; + +declare const generateLinearEasing: (easing: EasingFunction, duration: number, resolution?: number) => string; + +declare class MotionValueState { + latest: { + [name: string]: AnyResolvedKeyframe; + }; + private values; + set(name: string, value: MotionValue, render?: VoidFunction, computed?: MotionValue, useDefaultValueType?: boolean): () => void; + get(name: string): MotionValue | undefined; + destroy(): void; +} + +declare const addAttrValue: (element: HTMLElement | SVGElement, state: MotionValueState, key: string, value: MotionValue) => () => void; +declare const attrEffect: (subject: ElementOrSelector, values: Record>) => () => void; + +declare const propEffect: (subject: { + [key: string]: any; +}, values: Record>) => VoidFunction; + +declare const addStyleValue: (element: HTMLElement | SVGElement, state: MotionValueState, key: string, value: MotionValue) => () => void; +declare const styleEffect: (subject: ElementOrSelector, values: Record>) => () => void; + +declare const svgEffect: (subject: ElementOrSelector, values: Record>) => () => void; + +declare const frame: Batcher; +declare const cancelFrame: (process: Process) => void; +declare const frameData: FrameData; +declare const frameSteps: Steps; + +type Process = (data: FrameData) => void; +type Schedule = (process: Process, keepAlive?: boolean, immediate?: boolean) => Process; +interface Step { + schedule: Schedule; + cancel: (process: Process) => void; + process: (data: FrameData) => void; +} +type StepId = "setup" | "read" | "resolveKeyframes" | "preUpdate" | "update" | "preRender" | "render" | "postRender"; +type CancelProcess = (process: Process) => void; +type Batcher = { + [key in StepId]: Schedule; +}; +type Steps = { + [key in StepId]: Step; +}; +interface FrameData { + delta: number; + timestamp: number; + isProcessing: boolean; +} + +declare function createRenderBatcher(scheduleNextBatch: (callback: Function) => void, allowKeepAlive: boolean): { + schedule: Batcher; + cancel: (process: Process) => void; + state: FrameData; + steps: Steps; +}; + +declare const microtask: Batcher; +declare const cancelMicrotask: (process: Process) => void; + +/** + * An eventloop-synchronous alternative to performance.now(). + * + * Ensures that time measurements remain consistent within a synchronous context. + * Usually calling performance.now() twice within the same synchronous context + * will return different values which isn't useful for animations when we're usually + * trying to sync animations to the same frame. + */ +declare const time: { + now: () => number; + set: (newTime: number) => void; +}; + +declare const isDragging: { + x: boolean; + y: boolean; +}; +declare function isDragActive(): boolean; + +declare function setDragLock(axis: boolean | "x" | "y" | "lockDirection"): (() => void) | null; + +/** + * Passed in to pan event handlers like `onPan` the `PanInfo` object contains + * information about the current state of the tap gesture such as its + * `point`, `delta`, `offset` and `velocity`. + * + * ```jsx + * { + * console.log(info.point.x, info.point.y) + * }} /> + * ``` + * + * @public + */ +interface PanInfo { + /** + * Contains `x` and `y` values for the current pan position relative + * to the device or page. + * + * ```jsx + * function onPan(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @public + */ + point: Point; + /** + * Contains `x` and `y` values for the distance moved since + * the last event. + * + * ```jsx + * function onPan(event, info) { + * console.log(info.delta.x, info.delta.y) + * } + * + * + * ``` + * + * @public + */ + delta: Point; + /** + * Contains `x` and `y` values for the distance moved from + * the first pan event. + * + * ```jsx + * function onPan(event, info) { + * console.log(info.offset.x, info.offset.y) + * } + * + * + * ``` + * + * @public + */ + offset: Point; + /** + * Contains `x` and `y` values for the current velocity of the pointer, in px/ms. + * + * ```jsx + * function onPan(event, info) { + * console.log(info.velocity.x, info.velocity.y) + * } + * + * + * ``` + * + * @public + */ + velocity: Point; +} +type PanHandler = (event: Event, info: PanInfo) => void; + +type DragHandler = (event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => void; +type DragElastic = boolean | number | Partial; +interface ResolvedConstraints { + x: Partial; + y: Partial; +} +interface ResolvedElastic { + x: Axis; + y: Axis; +} + +type ElementOrSelector = Element | Element[] | NodeListOf | string; +interface WithQuerySelectorAll { + querySelectorAll: Element["querySelectorAll"]; +} +interface AnimationScope { + readonly current: T; + animations: any[]; +} +interface SelectorCache { + [key: string]: NodeListOf; +} +declare function resolveElements(elementOrSelector: ElementOrSelector, scope?: AnimationScope, selectorCache?: SelectorCache): Element[]; + +/** + * Options for event listeners. + * + * @public + */ +interface EventOptions { + /** + * Use passive event listeners. Doing so allows the browser to optimize + * scrolling performance by not allowing the use of `preventDefault()`. + * + * @default true + */ + passive?: boolean; + /** + * Remove the event listener after the first event. + * + * @default false + */ + once?: boolean; +} + +/** + * A function to be called when a hover gesture starts. + * + * This function can optionally return a function that will be called + * when the hover gesture ends. + * + * @public + */ +type OnHoverStartEvent = (element: Element, event: PointerEvent) => void | OnHoverEndEvent; +/** + * A function to be called when a hover gesture ends. + * + * @public + */ +type OnHoverEndEvent = (event: PointerEvent) => void; +/** + * Create a hover gesture. hover() is different to .addEventListener("pointerenter") + * in that it has an easier syntax, filters out polyfilled touch events, interoperates + * with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends. + * + * @public + */ +declare function hover(elementOrSelector: ElementOrSelector, onHoverStart: OnHoverStartEvent, options?: EventOptions): VoidFunction; + +interface PressGestureInfo { + success: boolean; +} +type OnPressEndEvent = (event: PointerEvent, info: PressGestureInfo) => void; +type OnPressStartEvent = (element: Element, event: PointerEvent) => OnPressEndEvent | void; + +interface PointerEventOptions extends EventOptions { + useGlobalTarget?: boolean; +} +/** + * Create a press gesture. + * + * Press is different to `"pointerdown"`, `"pointerup"` in that it + * automatically filters out secondary pointer events like right + * click and multitouch. + * + * It also adds accessibility support for keyboards, where + * an element with a press gesture will receive focus and + * trigger on Enter `"keydown"` and `"keyup"` events. + * + * This is different to a browser's `"click"` event, which does + * respond to keyboards but only for the `"click"` itself, rather + * than the press start and end/cancel. The element also needs + * to be focusable for this to work, whereas a press gesture will + * make an element focusable by default. + * + * @public + */ +declare function press(targetOrSelector: ElementOrSelector, onPressStart: OnPressStartEvent, options?: PointerEventOptions): VoidFunction; + +/** + * Recursively traverse up the tree to check whether the provided child node + * is the parent or a descendant of it. + * + * @param parent - Element to find + * @param child - Element to test against parent + */ +declare const isNodeOrChild: (parent: Element | null, child?: Element | null) => boolean; + +declare const isPrimaryPointer: (event: PointerEvent) => boolean; + +interface EventInfo { + point: Point; +} +/** + * A generic set of string/number values + */ +interface ResolvedValues { + [key: string]: AnyResolvedKeyframe; +} +type AnimationDefinition = VariantLabels | TargetAndTransition | TargetResolver; +/** + * An object that specifies values to animate to. Each value may be set either as + * a single value, or an array of values. + * + * It may also option contain these properties: + * + * - `transition`: Specifies transitions for all or individual values. + * - `transitionEnd`: Specifies values to set when the animation finishes. + * + * ```jsx + * const target = { + * x: "0%", + * opacity: 0, + * transition: { duration: 1 }, + * transitionEnd: { display: "none" } + * } + * ``` + * + * @public + */ +type TargetAndTransition = Target & { + transition?: Transition; + transitionEnd?: ResolvedValues; +}; +type TargetResolver = (custom: any, current: ResolvedValues, velocity: ResolvedValues) => TargetAndTransition | string; +/** + * Either a string, or array of strings, that reference variants defined via the `variants` prop. + * @public + */ +type VariantLabels = string | string[]; +type Variant = TargetAndTransition | TargetResolver; +interface Variants { + [key: string]: Variant; +} +/** + * @deprecated + */ +type LegacyAnimationControls = { + /** + * Subscribes a component's animation controls to this. + * + * @param controls - The controls to subscribe + * @returns An unsubscribe function. + */ + subscribe(visualElement: any): () => void; + /** + * Starts an animation on all linked components. + * + * @remarks + * + * ```jsx + * controls.start("variantLabel") + * controls.start({ + * x: 0, + * transition: { duration: 1 } + * }) + * ``` + * + * @param definition - Properties or variant label to animate to + * @param transition - Optional `transition` to apply to a variant + * @returns - A `Promise` that resolves when all animations have completed. + * + * @public + */ + start(definition: AnimationDefinition, transitionOverride?: Transition): Promise; + /** + * Instantly set to a set of properties or a variant. + * + * ```jsx + * // With properties + * controls.set({ opacity: 0 }) + * + * // With variants + * controls.set("hidden") + * ``` + * + * @privateRemarks + * We could perform a similar trick to `.start` where this can be called before mount + * and we maintain a list of of pending actions that get applied on mount. But the + * expectation of `set` is that it happens synchronously and this would be difficult + * to do before any children have even attached themselves. It's also poor practise + * and we should discourage render-synchronous `.start` calls rather than lean into this. + * + * @public + */ + set(definition: AnimationDefinition): void; + /** + * Stops animations on all linked components. + * + * ```jsx + * controls.stop() + * ``` + * + * @public + */ + stop(): void; + mount(): () => void; +}; +interface MotionNodeAnimationOptions { + /** + * Properties, variant label or array of variant labels to start in. + * + * Set to `false` to initialise with the values in `animate` (disabling the mount animation) + * + * ```jsx + * // As values + * + * + * // As variant + * + * + * // Multiple variants + * + * + * // As false (disable mount animation) + * + * ``` + */ + initial?: TargetAndTransition | VariantLabels | boolean; + /** + * Values to animate to, variant label(s), or `LegacyAnimationControls`. + * + * ```jsx + * // As values + * + * + * // As variant + * + * + * // Multiple variants + * + * + * // LegacyAnimationControls + * + * ``` + */ + animate?: TargetAndTransition | VariantLabels | boolean | LegacyAnimationControls; + /** + * A target to animate to when this component is removed from the tree. + * + * This component **must** be the first animatable child of an `AnimatePresence` to enable this exit animation. + * + * This limitation exists because React doesn't allow components to defer unmounting until after + * an animation is complete. Once this limitation is fixed, the `AnimatePresence` component will be unnecessary. + * + * ```jsx + * import { AnimatePresence, motion } from 'framer-motion' + * + * export const MyComponent = ({ isVisible }) => { + * return ( + * + * {isVisible && ( + * + * )} + * + * ) + * } + * ``` + */ + exit?: TargetAndTransition | VariantLabels; + /** + * Variants allow you to define animation states and organise them by name. They allow + * you to control animations throughout a component tree by switching a single `animate` prop. + * + * Using `transition` options like `delayChildren` and `when`, you can orchestrate + * when children animations play relative to their parent. + + * + * After passing variants to one or more `motion` component's `variants` prop, these variants + * can be used in place of values on the `animate`, `initial`, `whileFocus`, `whileTap` and `whileHover` props. + * + * ```jsx + * const variants = { + * active: { + * backgroundColor: "#f00" + * }, + * inactive: { + * backgroundColor: "#fff", + * transition: { duration: 2 } + * } + * } + * + * + * ``` + */ + variants?: Variants; + /** + * Default transition. If no `transition` is defined in `animate`, it will use the transition defined here. + * ```jsx + * const spring = { + * type: "spring", + * damping: 10, + * stiffness: 100 + * } + * + * + * ``` + */ + transition?: Transition; +} +interface MotionNodeEventOptions { + /** + * Callback with latest motion values, fired max once per frame. + * + * ```jsx + * function onUpdate(latest) { + * console.log(latest.x, latest.opacity) + * } + * + * + * ``` + */ + onUpdate?(latest: ResolvedValues): void; + /** + * Callback when animation defined in `animate` begins. + * + * The provided callback will be called with the triggering animation definition. + * If this is a variant, it'll be the variant name, and if a target object + * then it'll be the target object. + * + * This way, it's possible to figure out which animation has started. + * + * ```jsx + * function onStart() { + * console.log("Animation started") + * } + * + * + * ``` + */ + onAnimationStart?(definition: AnimationDefinition): void; + /** + * Callback when animation defined in `animate` is complete. + * + * The provided callback will be called with the triggering animation definition. + * If this is a variant, it'll be the variant name, and if a target object + * then it'll be the target object. + * + * This way, it's possible to figure out which animation has completed. + * + * ```jsx + * function onComplete() { + * console.log("Animation completed") + * } + * + * { + * console.log('Completed animating', definition) + * }} + * /> + * ``` + */ + onAnimationComplete?(definition: AnimationDefinition): void; + onBeforeLayoutMeasure?(box: Box): void; + onLayoutMeasure?(box: Box, prevBox: Box): void; + onLayoutAnimationStart?(): void; + onLayoutAnimationComplete?(): void; +} +interface MotionNodePanHandlers { + /** + * Callback function that fires when the pan gesture is recognised on this element. + * + * **Note:** For pan gestures to work correctly with touch input, the element needs + * touch scrolling to be disabled on either x/y or both axis with the + * [touch-action](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) CSS rule. + * + * ```jsx + * function onPan(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - A {@link PanInfo} object containing `x` and `y` values for: + * + * - `point`: Relative to the device or page. + * - `delta`: Distance moved since the last event. + * - `offset`: Offset from the original pan event. + * - `velocity`: Current velocity of the pointer. + */ + onPan?(event: PointerEvent, info: PanInfo): void; + /** + * Callback function that fires when the pan gesture begins on this element. + * + * ```jsx + * function onPanStart(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - A {@link PanInfo} object containing `x`/`y` values for: + * + * - `point`: Relative to the device or page. + * - `delta`: Distance moved since the last event. + * - `offset`: Offset from the original pan event. + * - `velocity`: Current velocity of the pointer. + */ + onPanStart?(event: PointerEvent, info: PanInfo): void; + /** + * Callback function that fires when we begin detecting a pan gesture. This + * is analogous to `onMouseStart` or `onTouchStart`. + * + * ```jsx + * function onPanSessionStart(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - An {@link EventInfo} object containing `x`/`y` values for: + * + * - `point`: Relative to the device or page. + */ + onPanSessionStart?(event: PointerEvent, info: EventInfo): void; + /** + * Callback function that fires when the pan gesture ends on this element. + * + * ```jsx + * function onPanEnd(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - A {@link PanInfo} object containing `x`/`y` values for: + * + * - `point`: Relative to the device or page. + * - `delta`: Distance moved since the last event. + * - `offset`: Offset from the original pan event. + * - `velocity`: Current velocity of the pointer. + */ + onPanEnd?(event: PointerEvent, info: PanInfo): void; +} +interface MotionNodeHoverHandlers { + /** + * Properties or variant label to animate to while the hover gesture is recognised. + * + * ```jsx + * + * ``` + */ + whileHover?: VariantLabels | TargetAndTransition; + /** + * Callback function that fires when pointer starts hovering over the component. + * + * ```jsx + * console.log('Hover starts')} /> + * ``` + */ + onHoverStart?(event: MouseEvent, info: EventInfo): void; + /** + * Callback function that fires when pointer stops hovering over the component. + * + * ```jsx + * console.log("Hover ends")} /> + * ``` + */ + onHoverEnd?(event: MouseEvent, info: EventInfo): void; +} +/** + * Passed in to tap event handlers like `onTap` the `TapInfo` object contains + * information about the tap gesture such as it‘s location. + * + * ```jsx + * function onTap(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @public + */ +interface TapInfo { + /** + * Contains `x` and `y` values for the tap gesture relative to the + * device or page. + * + * ```jsx + * function onTapStart(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @public + */ + point: Point; +} +interface MotionNodeTapHandlers { + /** + * Callback when the tap gesture successfully ends on this element. + * + * ```jsx + * function onTap(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - An {@link TapInfo} object containing `x` and `y` values for the `point` relative to the device or page. + */ + onTap?(event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo): void; + /** + * Callback when the tap gesture starts on this element. + * + * ```jsx + * function onTapStart(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - An {@link TapInfo} object containing `x` and `y` values for the `point` relative to the device or page. + */ + onTapStart?(event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo): void; + /** + * Callback when the tap gesture ends outside this element. + * + * ```jsx + * function onTapCancel(event, info) { + * console.log(info.point.x, info.point.y) + * } + * + * + * ``` + * + * @param event - The originating pointer event. + * @param info - An {@link TapInfo} object containing `x` and `y` values for the `point` relative to the device or page. + */ + onTapCancel?(event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo): void; + /** + * Properties or variant label to animate to while the component is pressed. + * + * ```jsx + * + * ``` + */ + whileTap?: VariantLabels | TargetAndTransition; + /** + * If `true`, the tap gesture will attach its start listener to window. + * + * Note: This is not supported publically. + */ + globalTapTarget?: boolean; +} +/** + * @deprecated - Use MotionNodeTapHandlers + */ +interface TapHandlers extends MotionNodeTapHandlers { +} +interface MotionNodeFocusHandlers { + /** + * Properties or variant label to animate to while the focus gesture is recognised. + * + * ```jsx + * + * ``` + */ + whileFocus?: VariantLabels | TargetAndTransition; +} +/** + * TODO: Replace with types from inView() + */ +type ViewportEventHandler = (entry: IntersectionObserverEntry | null) => void; +interface ViewportOptions { + root?: { + current: Element | null; + }; + once?: boolean; + margin?: string; + amount?: "some" | "all" | number; +} +interface MotionNodeViewportOptions { + whileInView?: VariantLabels | TargetAndTransition; + onViewportEnter?: ViewportEventHandler; + onViewportLeave?: ViewportEventHandler; + viewport?: ViewportOptions; +} +interface MotionNodeDraggableOptions { + /** + * Enable dragging for this element. Set to `false` by default. + * Set `true` to drag in both directions. + * Set `"x"` or `"y"` to only drag in a specific direction. + * + * ```jsx + * + * ``` + */ + drag?: boolean | "x" | "y"; + /** + * Properties or variant label to animate to while the drag gesture is recognised. + * + * ```jsx + * + * ``` + */ + whileDrag?: VariantLabels | TargetAndTransition; + /** + * If `true`, this will lock dragging to the initially-detected direction. Defaults to `false`. + * + * ```jsx + * + * ``` + */ + dragDirectionLock?: boolean; + /** + * Allows drag gesture propagation to child components. Set to `false` by + * default. + * + * ```jsx + * + * ``` + */ + dragPropagation?: boolean; + /** + * Applies constraints on the permitted draggable area. + * + * It can accept an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels. + * This will define a distance from the named edge of the draggable component. + * + * Alternatively, it can accept a `ref` to another component created with React's `useRef` hook. + * This `ref` should be passed both to the draggable component's `dragConstraints` prop, and the `ref` + * of the component you want to use as constraints. + * + * ```jsx + * // In pixels + * + * + * // As a ref to another component + * const MyComponent = () => { + * const constraintsRef = useRef(null) + * + * return ( + * + * + * + * ) + * } + * ``` + */ + dragConstraints?: false | Partial | { + current: Element | null; + }; + /** + * The degree of movement allowed outside constraints. 0 = no movement, 1 = + * full movement. + * + * Set to `0.5` by default. Can also be set as `false` to disable movement. + * + * By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set + * per constraint. Any missing values will be set to `0`. + * + * ```jsx + * + * ``` + */ + dragElastic?: DragElastic; + /** + * Apply momentum from the pan gesture to the component when dragging + * finishes. Set to `true` by default. + * + * ```jsx + * + * ``` + */ + dragMomentum?: boolean; + /** + * Allows you to change dragging inertia parameters. + * When releasing a draggable Frame, an animation with type `inertia` starts. The animation is based on your dragging velocity. This property allows you to customize it. + * See {@link https://motion.dev/docs/react-motion-component#dragtransition | Inertia} for all properties you can use. + * + * ```jsx + * + * ``` + */ + dragTransition?: InertiaOptions; + /** + * Usually, dragging is initiated by pressing down on a component and moving it. For some + * use-cases, for instance clicking at an arbitrary point on a video scrubber, we + * might want to initiate dragging from a different component than the draggable one. + * + * By creating a `dragControls` using the `useDragControls` hook, we can pass this into + * the draggable component's `dragControls` prop. It exposes a `start` method + * that can start dragging from pointer events on other components. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + */ + dragControls?: any; + /** + * If true, element will snap back to its origin when dragging ends. + * + * Enabling this is the equivalent of setting all `dragConstraints` axes to `0` + * with `dragElastic={1}`, but when used together `dragConstraints` can define + * a wider draggable area and `dragSnapToOrigin` will ensure the element + * animates back to its origin on release. + */ + dragSnapToOrigin?: boolean; + /** + * By default, if `drag` is defined on a component then an event listener will be attached + * to automatically initiate dragging when a user presses down on it. + * + * By setting `dragListener` to `false`, this event listener will not be created. + * + * ```jsx + * const dragControls = useDragControls() + * + * function startDrag(event) { + * dragControls.start(event, { snapToCursor: true }) + * } + * + * return ( + * <> + *
+ * + * + * ) + * ``` + */ + dragListener?: boolean; + /** + * If `dragConstraints` is set to a React ref, this callback will call with the measured drag constraints. + * + * @public + */ + onMeasureDragConstraints?: (constraints: BoundingBox) => BoundingBox | void; + /** + * Usually, dragging uses the layout project engine, and applies transforms to the underlying VisualElement. + * Passing MotionValues as _dragX and _dragY instead applies drag updates to these motion values. + * This allows you to manually control how updates from a drag gesture on an element is applied. + * + * @public + */ + _dragX?: MotionValue; + /** + * Usually, dragging uses the layout project engine, and applies transforms to the underlying VisualElement. + * Passing MotionValues as _dragX and _dragY instead applies drag updates to these motion values. + * This allows you to manually control how updates from a drag gesture on an element is applied. + * + * @public + */ + _dragY?: MotionValue; +} +interface MotionNodeDragHandlers { + /** + * Callback function that fires when dragging starts. + * + * ```jsx + * console.log(info.point.x, info.point.y) + * } + * /> + * ``` + * + * @public + */ + onDragStart?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void; + /** + * Callback function that fires when dragging ends. + * + * ```jsx + * console.log(info.point.x, info.point.y) + * } + * /> + * ``` + * + * @public + */ + onDragEnd?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void; + /** + * Callback function that fires when the component is dragged. + * + * ```jsx + * console.log(info.point.x, info.point.y) + * } + * /> + * ``` + * + * @public + */ + onDrag?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void; + /** + * Callback function that fires a drag direction is determined. + * + * ```jsx + * console.log(axis)} + * /> + * ``` + * + * @public + */ + onDirectionLock?(axis: "x" | "y"): void; + /** + * Callback function that fires when drag momentum/bounce transition finishes. + * + * ```jsx + * console.log('Drag transition complete')} + * /> + * ``` + * + * @public + */ + onDragTransitionEnd?(): void; +} +interface MotionNodeLayoutOptions { + /** + * If `true`, this component will automatically animate to its new position when + * its layout changes. + * + * ```jsx + * + * ``` + * + * This will perform a layout animation using performant transforms. Part of this technique + * involved animating an element's scale. This can introduce visual distortions on children, + * `boxShadow` and `borderRadius`. + * + * To correct distortion on immediate children, add `layout` to those too. + * + * `boxShadow` and `borderRadius` will automatically be corrected if they are already being + * animated on this component. Otherwise, set them directly via the `initial` prop. + * + * If `layout` is set to `"position"`, the size of the component will change instantly and + * only its position will animate. + * + * If `layout` is set to `"size"`, the position of the component will change instantly and + * only its size will animate. + * + * If `layout` is set to `"preserve-aspect"`, the component will animate size & position if + * the aspect ratio remains the same between renders, and just position if the ratio changes. + * + * @public + */ + layout?: boolean | "position" | "size" | "preserve-aspect"; + /** + * Enable shared layout transitions between different components with the same `layoutId`. + * + * When a component with a layoutId is removed from the React tree, and then + * added elsewhere, it will visually animate from the previous component's bounding box + * and its latest animated values. + * + * ```jsx + * {items.map(item => ( + * + * {item.name} + * {item.isSelected && } + * + * ))} + * ``` + * + * If the previous component remains in the tree it will crossfade with the new component. + * + * @public + */ + layoutId?: string; + /** + * A callback that will fire when a layout animation on this component starts. + * + * @public + */ + onLayoutAnimationStart?(): void; + /** + * A callback that will fire when a layout animation on this component completes. + * + * @public + */ + onLayoutAnimationComplete?(): void; + /** + * @public + */ + layoutDependency?: any; + /** + * Whether a projection node should measure its scroll when it or its descendants update their layout. + * + * @public + */ + layoutScroll?: boolean; + /** + * Whether an element should be considered a "layout root", where + * all children will be forced to resolve relatively to it. + * Currently used for `position: sticky` elements in Framer. + */ + layoutRoot?: boolean; + /** + * Attached to a portal root to ensure we attach the child to the document root and don't + * perform scale correction on it. + */ + "data-framer-portal-id"?: string; + /** + * By default, shared layout elements will crossfade. By setting this + * to `false`, this element will take its default opacity throughout the animation. + */ + layoutCrossfade?: boolean; +} +/** + * @deprecated - Use MotionNodeDragHandlers/MotionNodeDraggableOptions + */ +interface DraggableProps extends MotionNodeDragHandlers, MotionNodeDraggableOptions { +} +type TransformTemplate = (transform: TransformProperties, generatedTransform: string) => string; +interface MotionNodeAdvancedOptions { + /** + * Custom data to use to resolve dynamic variants differently for each animating component. + * + * ```jsx + * const variants = { + * visible: (custom) => ({ + * opacity: 1, + * transition: { delay: custom * 0.2 } + * }) + * } + * + * + * + * + * ``` + * + * @public + */ + custom?: any; + /** + * @public + * Set to `false` to prevent inheriting variant changes from its parent. + */ + inherit?: boolean; + /** + * @public + * Set to `false` to prevent throwing an error when a `motion` component is used within a `LazyMotion` set to strict. + */ + ignoreStrict?: boolean; + /** + * Provide a set of motion values to perform animations on. + */ + values?: { + [key: string]: MotionValue | MotionValue; + }; + /** + * By default, Motion generates a `transform` property with a sensible transform order. `transformTemplate` + * can be used to create a different order, or to append/preprend the automatically generated `transform` property. + * + * ```jsx + * `rotate(${rotate}deg) translateX(${x}px)` + * } + * /> + * ``` + * + * @param transform - The latest animated transform props. + * @param generatedTransform - The transform string as automatically generated by Motion + * + * @public + */ + transformTemplate?: TransformTemplate; + "data-framer-appear-id"?: string; +} +interface MotionNodeOptions extends MotionNodeAnimationOptions, MotionNodeEventOptions, MotionNodePanHandlers, MotionNodeTapHandlers, MotionNodeHoverHandlers, MotionNodeFocusHandlers, MotionNodeViewportOptions, MotionNodeDragHandlers, MotionNodeDraggableOptions, MotionNodeLayoutOptions, MotionNodeAdvancedOptions { +} + +declare function defaultTransformValue(name: string): number; +declare function parseValueFromTransform(transform: string | undefined, name: string): number; +declare const readTransformValue: (instance: HTMLElement, name: string) => number; + +declare function getComputedStyle(element: HTMLElement | SVGElement, name: string): string; + +declare function setStyle(element: HTMLElement | SVGElement, name: string, value: AnyResolvedKeyframe): void; + +declare const positionalKeys: Set; + +/** + * Generate a list of every possible transform key. + */ +declare const transformPropOrder: string[]; +/** + * A quick lookup for transform props. + */ +declare const transformProps: Set; + +interface ResizeInfo { + width: number; + height: number; +} +type ResizeHandler = (element: I, info: ResizeInfo) => void; +type WindowResizeHandler = (info: ResizeInfo) => void; + +declare function resize(onResize: WindowResizeHandler): VoidFunction; +declare function resize(target: ElementOrSelector, onResize: ResizeHandler): VoidFunction; + +type Update = (progress: number) => void; +declare function observeTimeline(update: Update, timeline: ProgressTimeline): () => void; + +declare const stepsOrder: StepId[]; +type StepNames = (typeof stepsOrder)[number]; + +interface Summary { + min: number; + max: number; + avg: number; +} +type FrameloopStatNames = "rate" | StepNames; +interface Stats { + frameloop: { + [key in FrameloopStatNames]: T; + }; + animations: { + mainThread: T; + waapi: T; + layout: T; + }; + layoutProjection: { + nodes: T; + calculatedTargetDeltas: T; + calculatedProjections: T; + }; +} +type StatsBuffer = number[]; +type FrameStats = Stats; +type StatsRecording = Stats; +type StatsSummary = Stats; + +declare function reportStats(): StatsSummary; +declare function recordStats(): typeof reportStats; + +declare const activeAnimations: { + layout: number; + mainThread: number; + waapi: number; +}; + +type InactiveStatsBuffer = { + value: null; + addProjectionMetrics: null; +}; +type ActiveStatsBuffer = { + value: StatsRecording; + addProjectionMetrics: (metrics: { + nodes: number; + calculatedTargetDeltas: number; + calculatedProjections: number; + }) => void; +}; +declare const statsBuffer: InactiveStatsBuffer | ActiveStatsBuffer; + +type Mixer = (p: number) => T; +type MixerFactory = (a: T, b: T) => Mixer; + +interface InterpolateOptions { + clamp?: boolean; + ease?: EasingFunction | EasingFunction[]; + mixer?: MixerFactory; +} +/** + * Create a function that maps from a numerical input array to a generic output array. + * + * Accepts: + * - Numbers + * - Colors (hex, hsl, hsla, rgb, rgba) + * - Complex (combinations of one or more numbers or strings) + * + * ```jsx + * const mixColor = interpolate([0, 1], ['#fff', '#000']) + * + * mixColor(0.5) // 'rgba(128, 128, 128, 1)' + * ``` + * + * TODO Revisit this approach once we've moved to data models for values, + * probably not needed to pregenerate mixer functions. + * + * @public + */ +declare function interpolate(input: number[], output: T[], { clamp: isClamp, ease, mixer }?: InterpolateOptions): (v: number) => T; + +/** + * Checks if an element is an HTML element in a way + * that works across iframes + */ +declare function isHTMLElement(element: unknown): element is HTMLElement; + +/** + * Checks if an element is an SVG element in a way + * that works across iframes + */ +declare function isSVGElement(element: unknown): element is SVGElement; + +/** + * Checks if an element is specifically an SVGSVGElement (the root SVG element) + * in a way that works across iframes + */ +declare function isSVGSVGElement(element: unknown): element is SVGSVGElement; + +declare function mix(from: T, to: T): Mixer; +declare function mix(from: number, to: number, p: number): number; + +type Transformer = (v: any) => any; +type ValueType = { + test: (v: any) => boolean; + parse: (v: any) => any; + transform?: Transformer; + createTransformer?: (template: string) => Transformer; + default?: any; + getAnimatableNone?: (v: any) => any; +}; +type NumberMap = { + [key: string]: number; +}; +type RGBA = { + red: number; + green: number; + blue: number; + alpha: number; +}; +type HSLA = { + hue: number; + saturation: number; + lightness: number; + alpha: number; +}; +type Color = HSLA | RGBA; + +declare const mixLinearColor: (from: number, to: number, v: number) => number; +declare const mixColor: (from: Color | string, to: Color | string) => (p: number) => string | Color; + +type MixableArray = Array; +interface MixableObject { + [key: string]: AnyResolvedKeyframe | RGBA | HSLA; +} +declare function getMixer(a: T): ((from: string | Color, to: string | Color) => (p: number) => string | Color) | ((origin: AnyResolvedKeyframe, target: AnyResolvedKeyframe) => Function) | typeof mixArray | typeof mixObject; +declare function mixArray(a: MixableArray, b: MixableArray): (p: number) => (string | number | RGBA | HSLA)[]; +declare function mixObject(a: MixableObject, b: MixableObject): (v: number) => { + [x: string]: AnyResolvedKeyframe | RGBA | HSLA; +}; +declare const mixComplex: (origin: AnyResolvedKeyframe, target: AnyResolvedKeyframe) => Function; + +declare function mixImmediate(a: T, b: T): (p: number) => T; + +declare const mixNumber: (from: number, to: number, progress: number) => number; + +declare const invisibleValues: Set; +/** + * Returns a function that, when provided a progress value between 0 and 1, + * will return the "none" or "hidden" string only when the progress is that of + * the origin or target. + */ +declare function mixVisibility(origin: string, target: string): (p: number) => string; + +type StaggerOrigin = "first" | "last" | "center" | number; +type StaggerOptions = { + startDelay?: number; + from?: StaggerOrigin; + ease?: Easing; +}; +declare function getOriginIndex(from: StaggerOrigin, total: number): number; +declare function stagger(duration?: number, { startDelay, from, ease }?: StaggerOptions): DynamicOption; + +/** + * Add the ability for test suites to manually set support flags + * to better test more environments. + */ +declare const supportsFlags: Record; + +declare const supportsLinearEasing: () => boolean; + +declare global { + interface Window { + ScrollTimeline: ScrollTimeline; + } +} +declare class ScrollTimeline implements ProgressTimeline { + constructor(options: ScrollOptions); + currentTime: null | { + value: number; + }; + cancel?: VoidFunction; +} +declare const supportsScrollTimeline: () => boolean; + +/** + * @public + */ +interface TransformOptions { + /** + * Clamp values to within the given range. Defaults to `true` + * + * @public + */ + clamp?: boolean; + /** + * Easing functions to use on the interpolations between each value in the input and output ranges. + * + * If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition **between** each. + * + * @public + */ + ease?: EasingFunction | EasingFunction[]; + /** + * Provide a function that can interpolate between any two values in the provided range. + * + * @public + */ + mixer?: (from: T, to: T) => (v: number) => any; +} +/** + * Transforms numbers into other values by mapping them from an input range to an output range. + * Returns the type of the input provided. + * + * @remarks + * + * Given an input range of `[0, 200]` and an output range of + * `[0, 1]`, this function will return a value between `0` and `1`. + * The input range must be a linear series of numbers. The output range + * can be any supported value type, such as numbers, colors, shadows, arrays, objects and more. + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * export function MyComponent() { + * const inputRange = [0, 200] + * const outputRange = [0, 1] + * const output = transform(100, inputRange, outputRange) + * + * // Returns 0.5 + * return
{output}
+ * } + * ``` + * + * @param inputValue - A number to transform between the input and output ranges. + * @param inputRange - A linear series of numbers (either all increasing or decreasing). + * @param outputRange - A series of numbers, colors, strings, or arrays/objects of those. Must be the same length as `inputRange`. + * @param options - Clamp: Clamp values to within the given range. Defaults to `true`. + * + * @public + */ +declare function transform(inputValue: number, inputRange: number[], outputRange: T[], options?: TransformOptions): T; +/** + * + * Transforms numbers into other values by mapping them from an input range to an output range. + * + * Given an input range of `[0, 200]` and an output range of + * `[0, 1]`, this function will return a value between `0` and `1`. + * The input range must be a linear series of numbers. The output range + * can be any supported value type, such as numbers, colors, shadows, arrays, objects and more. + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * export function MyComponent() { + * const inputRange = [-200, -100, 100, 200] + * const outputRange = [0, 1, 1, 0] + * const convertRange = transform(inputRange, outputRange) + * const output = convertRange(-150) + * + * // Returns 0.5 + * return
{output}
+ * } + * + * ``` + * + * @param inputRange - A linear series of numbers (either all increasing or decreasing). + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - Clamp: clamp values to within the given range. Defaults to `true`. + * + * @public + */ +declare function transform(inputRange: number[], outputRange: T[], options?: TransformOptions): (inputValue: number) => T; + +type MapInputRange = number[]; +/** + * Create a `MotionValue` that maps the output of another `MotionValue` by + * mapping it from one range of values into another. + * + * @remarks + * + * Given an input range of `[-200, -100, 100, 200]` and an output range of + * `[0, 1, 1, 0]`, the returned `MotionValue` will: + * + * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`. + * - When provided a value between `-100` and `100`, will return `1`. + * - When provided a value between `100` and `200`, will return a value between `1` and `0` + * + * The input range must be a linear series of numbers. The output range + * can be any value type supported by Motion: numbers, colors, shadows, etc. + * + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * const x = motionValue(0) + * const xRange = [-200, -100, 100, 200] + * const opacityRange = [0, 1, 1, 0] + * const opacity = mapValue(x, xRange, opacityRange) + * ``` + * + * @param inputValue - `MotionValue` + * @param inputRange - A linear series of numbers (either all increasing or decreasing) + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - + * + * - clamp: boolean. Clamp values to within the given range. Defaults to `true` + * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each. + * + * @returns `MotionValue` + * + * @public + */ +declare function mapValue(inputValue: MotionValue, inputRange: MapInputRange, outputRange: O[], options?: TransformOptions): MotionValue; + +/** + * Create a `MotionValue` that animates to its latest value using a spring. + * Can either be a value or track another `MotionValue`. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ +declare function springValue(source: T | MotionValue, options?: SpringOptions): MotionValue; +declare function attachSpring(value: MotionValue, source: T | MotionValue, options?: SpringOptions): VoidFunction | undefined; + +type TransformInputRange = number[]; +type SingleTransformer = (input: I) => O; +type MultiTransformer = (input: I[]) => O; +type ValueTransformer = SingleTransformer | MultiTransformer; +/** + * Create a `MotionValue` that transforms the output of other `MotionValue`s by + * passing their latest values through a transform function. + * + * Whenever a `MotionValue` referred to in the provided function is updated, + * it will be re-evaluated. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ +declare function transformValue(transform: () => O): MotionValue; + +declare const color: { + test: (v: any) => boolean; + parse: (v: any) => RGBA | HSLA; + transform: (v: HSLA | RGBA | string) => string; + getAnimatableNone: (v: string) => string; +}; + +declare function parseHex(v: string): RGBA; +declare const hex: { + test: (v: any) => boolean; + parse: typeof parseHex; + transform: ({ red, green, blue, alpha }: RGBA) => string; +}; + +declare const hsla: { + test: (v: any) => boolean; + parse: (v: string | Color) => HSLA; + transform: ({ hue, saturation, lightness, alpha }: HSLA) => string; +}; + +declare function hslaToRgba({ hue, saturation, lightness, alpha }: HSLA): RGBA; + +declare const rgbUnit: { + transform: (v: number) => number; + test: (v: number) => boolean; + parse: typeof parseFloat; +}; +declare const rgba: { + test: (v: any) => boolean; + parse: (v: string | Color) => RGBA; + transform: ({ red, green, blue, alpha }: RGBA) => string; +}; + +declare function test(v: any): boolean; +type ComplexValues = Array; +interface ValueIndexes { + color: number[]; + number: number[]; + var: number[]; +} +interface ComplexValueInfo { + values: ComplexValues; + split: string[]; + indexes: ValueIndexes; + types: Array; +} +declare function analyseComplexValue(value: AnyResolvedKeyframe): ComplexValueInfo; +declare function parseComplexValue(v: AnyResolvedKeyframe): ComplexValues; +declare function createTransformer(source: AnyResolvedKeyframe): (v: Array) => string; +declare function getAnimatableNone$1(v: AnyResolvedKeyframe): string; +declare const complex: { + test: typeof test; + parse: typeof parseComplexValue; + createTransformer: typeof createTransformer; + getAnimatableNone: typeof getAnimatableNone$1; +}; + +/** + * A list of value types commonly used for dimensions + */ +declare const dimensionValueTypes: ({ + test: (v: number) => boolean; + parse: typeof parseFloat; + transform: (v: number) => number; +} | { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: string | number) => string; +} | ValueType)[]; +/** + * Tests a dimensional value against the list of dimension ValueTypes + */ +declare const findDimensionValueType: (v: any) => { + test: (v: number) => boolean; + parse: typeof parseFloat; + transform: (v: number) => number; +} | { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: string | number) => string; +} | ValueType | undefined; + +interface ValueTypeMap { + [key: string]: ValueType; +} + +/** + * A map of default value types for common values + */ +declare const defaultValueTypes: ValueTypeMap; +/** + * Gets the default ValueType for the provided value key + */ +declare const getDefaultValueType: (key: string) => ValueType; + +declare const numberValueTypes: ValueTypeMap; + +declare const transformValueTypes: ValueTypeMap; + +declare const number: { + test: (v: number) => boolean; + parse: typeof parseFloat; + transform: (v: number) => number; +}; +declare const alpha: { + transform: (v: number) => number; + test: (v: number) => boolean; + parse: typeof parseFloat; +}; +declare const scale: { + default: number; + test: (v: number) => boolean; + parse: typeof parseFloat; + transform: (v: number) => number; +}; + +declare const degrees: { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: number | string) => string; +}; +declare const percent: { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: number | string) => string; +}; +declare const px: { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: number | string) => string; +}; +declare const vh: { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: number | string) => string; +}; +declare const vw: { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: number | string) => string; +}; +declare const progressPercentage: { + parse: (v: string) => number; + transform: (v: number) => string; + test: (v: AnyResolvedKeyframe) => boolean; +}; + +/** + * Tests a provided value against a ValueType + */ +declare const testValueType: (v: any) => (type: ValueType) => boolean; + +declare function getAnimatableNone(key: string, value: string): any; + +/** + * Tests a value against the list of ValueTypes + */ +declare const findValueType: (v: any) => { + test: (v: number) => boolean; + parse: typeof parseFloat; + transform: (v: number) => number; +} | { + test: (v: AnyResolvedKeyframe) => boolean; + parse: typeof parseFloat; + transform: (v: string | number) => string; +} | ValueType | { + test: (v: any) => boolean; + parse: (v: any) => RGBA | HSLA; + transform: (v: string | RGBA | HSLA) => string; + getAnimatableNone: (v: string) => string; +} | { + test: (v: any) => boolean; + parse: (v: AnyResolvedKeyframe) => ComplexValues; + createTransformer: (source: AnyResolvedKeyframe) => (v: (string | number | Color)[]) => string; + getAnimatableNone: (v: AnyResolvedKeyframe) => string; +} | undefined; + +/** + * Provided a value and a ValueType, returns the value as that value type. + */ +declare const getValueAsType: (value: any, type?: ValueType) => any; + +declare const isMotionValue: (value: any) => value is MotionValue; + +type ViewTransitionAnimationDefinition = { + keyframes: DOMKeyframesDefinition; + options: AnimationOptions; +}; +type ViewTransitionTarget = { + layout?: ViewTransitionAnimationDefinition; + enter?: ViewTransitionAnimationDefinition; + exit?: ViewTransitionAnimationDefinition; + new?: ViewTransitionAnimationDefinition; + old?: ViewTransitionAnimationDefinition; +}; +type ViewTransitionOptions = AnimationOptions & { + interrupt?: "wait" | "immediate"; +}; +type ViewTransitionTargetDefinition = string | Element; + +declare class ViewTransitionBuilder { + private currentSubject; + targets: Map; + update: () => void | Promise; + options: ViewTransitionOptions; + notifyReady: (value: GroupAnimation) => void; + private readyPromise; + constructor(update: () => void | Promise, options?: ViewTransitionOptions); + get(subject: ViewTransitionTargetDefinition): this; + layout(keyframes: DOMKeyframesDefinition, options?: AnimationOptions): this; + new(keyframes: DOMKeyframesDefinition, options?: AnimationOptions): this; + old(keyframes: DOMKeyframesDefinition, options?: AnimationOptions): this; + enter(keyframes: DOMKeyframesDefinition, options?: AnimationOptions): this; + exit(keyframes: DOMKeyframesDefinition, options?: AnimationOptions): this; + crossfade(options?: AnimationOptions): this; + updateTarget(target: "enter" | "exit" | "layout" | "new" | "old", keyframes: DOMKeyframesDefinition, options?: AnimationOptions): void; + then(resolve: () => void, reject?: () => void): Promise; +} +declare function animateView(update: () => void | Promise, defaultOptions?: ViewTransitionOptions): ViewTransitionBuilder; + +/** + * @deprecated + * + * Import as `frame` instead. + */ +declare const sync: Batcher; +/** + * @deprecated + * + * Use cancelFrame(callback) instead. + */ +declare const cancelSync: Record void>; + +export { type AcceptedAnimations, type ActiveStatsBuffer, type AnimationDefinition, type AnimationGeneratorName, type AnimationGeneratorType, type AnimationOptions, type AnimationOrchestrationOptions, type AnimationPlaybackControls, type AnimationPlaybackControlsWithThen, type AnimationPlaybackLifecycles, type AnimationPlaybackOptions, type AnimationScope, type AnimationState, type AnyResolvedKeyframe, AsyncMotionValueAnimation, type Batcher, type CSSStyleDeclarationWithTransform, type CSSVariableName, type CSSVariableToken, type CancelProcess, type Color, type ComplexValueInfo, type ComplexValues, type DOMKeyframesDefinition, DOMKeyframesResolver, type DOMValueAnimationOptions, type DecayOptions, type DragElastic, type DragHandler, type DraggableProps, type DurationSpringOptions, type DynamicOption, type ElementOrSelector, type EventInfo, type EventOptions, type FrameData, type FrameStats, type GeneratorFactory, type GeneratorFactoryFunction, GroupAnimation, GroupAnimationWithThen, type GroupedAnimations, type HSLA, type InactiveStatsBuffer, type InertiaOptions, type InterpolateOptions, JSAnimation, type KeyframeGenerator, type KeyframeOptions, KeyframeResolver, type LegacyAnimationControls, type MapInputRange, type Mixer, type MixerFactory, type MotionNodeAdvancedOptions, type MotionNodeAnimationOptions, type MotionNodeDragHandlers, type MotionNodeDraggableOptions, type MotionNodeEventOptions, type MotionNodeFocusHandlers, type MotionNodeHoverHandlers, type MotionNodeLayoutOptions, type MotionNodeOptions, type MotionNodePanHandlers, type MotionNodeTapHandlers, type MotionNodeViewportOptions, MotionValue, type MotionValueEventCallbacks, type MotionValueOptions, type MultiTransformer, NativeAnimation, NativeAnimationExtended, type NativeAnimationOptions, type NativeAnimationOptionsExtended, NativeAnimationWrapper, type NumberMap, type OnHoverEndEvent, type OnHoverStartEvent, type OnKeyframesResolved, type OnPressEndEvent, type OnPressStartEvent, type Owner, type PanHandler, type PanInfo, type PassiveEffect, type PointerEventOptions, type PressGestureInfo, type Process, type ProgressTimeline, type RGBA, type RepeatType, type ResolvedConstraints, type ResolvedElastic, type ResolvedKeyframes, type ResolvedValueKeyframe, type ResolvedValues, type SVGAttributes, type SVGForcedAttrKeyframesDefinition, type SVGForcedAttrProperties, type SVGForcedAttrTransitions, type SVGKeyframesDefinition, type SVGPathKeyframesDefinition, type SVGPathProperties, type SVGPathTransitions, type SVGTransitions, type Schedule, type SelectorCache, type SingleTransformer, type Spring, type SpringOptions, type StaggerOptions, type StaggerOrigin, type StartAnimation, type Stats, type StatsBuffer, type StatsRecording, type StatsSummary, type Step, type StepId, type Steps, type StyleKeyframesDefinition, type StyleTransitions, type Subscriber, type Summary, type TapHandlers, type TapInfo, type Target, type TargetAndTransition, type TargetResolver, type TimelineWithFallback, type TransformInputRange, type TransformOptions, type TransformProperties, type TransformTemplate, type Transformer, type Transition, type TransitionWithValueOverrides, type Tween, type UnresolvedKeyframes, type UnresolvedValueKeyframe, type ValueAnimationOptions, type ValueAnimationOptionsWithRenderContext, type ValueAnimationTransition, type ValueAnimationWithDynamicDelay, type ValueIndexes, type ValueKeyframe, type ValueKeyframesDefinition, type ValueTransformer, type ValueTransition, type ValueType, type ValueTypeMap, type VariableKeyframesDefinition, type VariableTransitions, type Variant, type VariantLabels, type Variants, type VelocityOptions, type ViewTransitionAnimationDefinition, ViewTransitionBuilder, type ViewTransitionOptions, type ViewTransitionTarget, type ViewTransitionTargetDefinition, type ViewportEventHandler, type ViewportOptions, type WithQuerySelectorAll, acceleratedValues, activeAnimations, addAttrValue, addStyleValue, alpha, analyseComplexValue, animateValue, animateView, animationMapKey, applyPxDefaults, attachSpring, attrEffect, calcGeneratorDuration, cancelFrame, cancelMicrotask, cancelSync, collectMotionValues, color, complex, convertOffsetToTimes, createGeneratorEasing, createRenderBatcher, cubicBezierAsString, defaultEasing, defaultOffset, defaultTransformValue, defaultValueTypes, degrees, dimensionValueTypes, fillOffset, fillWildcards, findDimensionValueType, findValueType, flushKeyframeResolvers, frame, frameData, frameSteps, generateLinearEasing, getAnimatableNone, getAnimationMap, getComputedStyle, getDefaultValueType, getMixer, getOriginIndex, getValueAsType, getValueTransition, getVariableValue, hex, hover, hsla, hslaToRgba, inertia, interpolate, invisibleValues, isCSSVariableName, isCSSVariableToken, isDragActive, isDragging, isGenerator, isHTMLElement, isMotionValue, isNodeOrChild, isPrimaryPointer, isSVGElement, isSVGSVGElement, isWaapiSupportedEasing, keyframes, mapEasingToNativeEasing, mapValue, maxGeneratorDuration, microtask, mix, mixArray, mixColor, mixComplex, mixImmediate, mixLinearColor, mixNumber, mixObject, mixVisibility, motionValue, number, numberValueTypes, observeTimeline, parseCSSVariable, parseValueFromTransform, percent, positionalKeys, press, progressPercentage, propEffect, px, readTransformValue, recordStats, resize, resolveElements, rgbUnit, rgba, scale, setDragLock, setStyle, spring, springValue, stagger, startWaapiAnimation, statsBuffer, styleEffect, supportedWaapiEasing, supportsBrowserAnimation, supportsFlags, supportsLinearEasing, supportsPartialKeyframes, supportsScrollTimeline, svgEffect, sync, testValueType, time, transform, transformPropOrder, transformProps, transformValue, transformValueTypes, vh, vw }; diff --git a/node_modules/motion-dom/dist/motion-dom.dev.js b/node_modules/motion-dom/dist/motion-dom.dev.js new file mode 100644 index 00000000..6d36e633 --- /dev/null +++ b/node_modules/motion-dom/dist/motion-dom.dev.js @@ -0,0 +1,4774 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('motion-utils')) : + typeof define === 'function' && define.amd ? define(['exports', 'motion-utils'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MotionDom = {}, global.MotionUtils)); +})(this, (function (exports, motionUtils) { 'use strict'; + + const stepsOrder = [ + "setup", // Compute + "read", // Read + "resolveKeyframes", // Write/Read/Write/Read + "preUpdate", // Compute + "update", // Compute + "preRender", // Compute + "render", // Write + "postRender", // Compute + ]; + + const statsBuffer = { + value: null, + addProjectionMetrics: null, + }; + + function createRenderStep(runNextFrame, stepName) { + /** + * We create and reuse two queues, one to queue jobs for the current frame + * and one for the next. We reuse to avoid triggering GC after x frames. + */ + let thisFrame = new Set(); + let nextFrame = new Set(); + /** + * Track whether we're currently processing jobs in this step. This way + * we can decide whether to schedule new jobs for this frame or next. + */ + let isProcessing = false; + let flushNextFrame = false; + /** + * A set of processes which were marked keepAlive when scheduled. + */ + const toKeepAlive = new WeakSet(); + let latestFrameData = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + let numCalls = 0; + function triggerCallback(callback) { + if (toKeepAlive.has(callback)) { + step.schedule(callback); + runNextFrame(); + } + numCalls++; + callback(latestFrameData); + } + const step = { + /** + * Schedule a process to run on the next frame. + */ + schedule: (callback, keepAlive = false, immediate = false) => { + const addToCurrentFrame = immediate && isProcessing; + const queue = addToCurrentFrame ? thisFrame : nextFrame; + if (keepAlive) + toKeepAlive.add(callback); + if (!queue.has(callback)) + queue.add(callback); + return callback; + }, + /** + * Cancel the provided callback from running on the next frame. + */ + cancel: (callback) => { + nextFrame.delete(callback); + toKeepAlive.delete(callback); + }, + /** + * Execute all schedule callbacks. + */ + process: (frameData) => { + latestFrameData = frameData; + /** + * If we're already processing we've probably been triggered by a flushSync + * inside an existing process. Instead of executing, mark flushNextFrame + * as true and ensure we flush the following frame at the end of this one. + */ + if (isProcessing) { + flushNextFrame = true; + return; + } + isProcessing = true; + [thisFrame, nextFrame] = [nextFrame, thisFrame]; + // Execute this frame + thisFrame.forEach(triggerCallback); + /** + * If we're recording stats then + */ + if (stepName && statsBuffer.value) { + statsBuffer.value.frameloop[stepName].push(numCalls); + } + numCalls = 0; + // Clear the frame so no callbacks remain. This is to avoid + // memory leaks should this render step not run for a while. + thisFrame.clear(); + isProcessing = false; + if (flushNextFrame) { + flushNextFrame = false; + step.process(frameData); + } + }, + }; + return step; + } + + const maxElapsed = 40; + function createRenderBatcher(scheduleNextBatch, allowKeepAlive) { + let runNextFrame = false; + let useDefaultElapsed = true; + const state = { + delta: 0.0, + timestamp: 0.0, + isProcessing: false, + }; + const flagRunNextFrame = () => (runNextFrame = true); + const steps = stepsOrder.reduce((acc, key) => { + acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined); + return acc; + }, {}); + const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps; + const processBatch = () => { + const timestamp = motionUtils.MotionGlobalConfig.useManualTiming + ? state.timestamp + : performance.now(); + runNextFrame = false; + if (!motionUtils.MotionGlobalConfig.useManualTiming) { + state.delta = useDefaultElapsed + ? 1000 / 60 + : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1); + } + state.timestamp = timestamp; + state.isProcessing = true; + // Unrolled render loop for better per-frame performance + setup.process(state); + read.process(state); + resolveKeyframes.process(state); + preUpdate.process(state); + update.process(state); + preRender.process(state); + render.process(state); + postRender.process(state); + state.isProcessing = false; + if (runNextFrame && allowKeepAlive) { + useDefaultElapsed = false; + scheduleNextBatch(processBatch); + } + }; + const wake = () => { + runNextFrame = true; + useDefaultElapsed = true; + if (!state.isProcessing) { + scheduleNextBatch(processBatch); + } + }; + const schedule = stepsOrder.reduce((acc, key) => { + const step = steps[key]; + acc[key] = (process, keepAlive = false, immediate = false) => { + if (!runNextFrame) + wake(); + return step.schedule(process, keepAlive, immediate); + }; + return acc; + }, {}); + const cancel = (process) => { + for (let i = 0; i < stepsOrder.length; i++) { + steps[stepsOrder[i]].cancel(process); + } + }; + return { schedule, cancel, state, steps }; + } + + const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : motionUtils.noop, true); + + let now; + function clearTime() { + now = undefined; + } + /** + * An eventloop-synchronous alternative to performance.now(). + * + * Ensures that time measurements remain consistent within a synchronous context. + * Usually calling performance.now() twice within the same synchronous context + * will return different values which isn't useful for animations when we're usually + * trying to sync animations to the same frame. + */ + const time = { + now: () => { + if (now === undefined) { + time.set(frameData.isProcessing || motionUtils.MotionGlobalConfig.useManualTiming + ? frameData.timestamp + : performance.now()); + } + return now; + }, + set: (newTime) => { + now = newTime; + queueMicrotask(clearTime); + }, + }; + + const activeAnimations = { + layout: 0, + mainThread: 0, + waapi: 0, + }; + + const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token); + const isCSSVariableName = + /*@__PURE__*/ checkStringStartsWith("--"); + const startsAsVariableToken = + /*@__PURE__*/ checkStringStartsWith("var(--"); + const isCSSVariableToken = (value) => { + const startsWithToken = startsAsVariableToken(value); + if (!startsWithToken) + return false; + // Ensure any comments are stripped from the value as this can harm performance of the regex. + return singleCssVariableRegex.test(value.split("/*")[0].trim()); + }; + const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu; + + const number = { + test: (v) => typeof v === "number", + parse: parseFloat, + transform: (v) => v, + }; + const alpha = { + ...number, + transform: (v) => motionUtils.clamp(0, 1, v), + }; + const scale = { + ...number, + default: 1, + }; + + // If this number is a decimal, make it just five decimal places + // to avoid exponents + const sanitize = (v) => Math.round(v * 100000) / 100000; + + const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu; + + function isNullish(v) { + return v == null; + } + + const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu; + + /** + * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000, + * but false if a number or multiple colors + */ + const isColorString = (type, testProp) => (v) => { + return Boolean((typeof v === "string" && + singleColorRegex.test(v) && + v.startsWith(type)) || + (testProp && + !isNullish(v) && + Object.prototype.hasOwnProperty.call(v, testProp))); + }; + const splitColor = (aName, bName, cName) => (v) => { + if (typeof v !== "string") + return v; + const [a, b, c, alpha] = v.match(floatRegex); + return { + [aName]: parseFloat(a), + [bName]: parseFloat(b), + [cName]: parseFloat(c), + alpha: alpha !== undefined ? parseFloat(alpha) : 1, + }; + }; + + const clampRgbUnit = (v) => motionUtils.clamp(0, 255, v); + const rgbUnit = { + ...number, + transform: (v) => Math.round(clampRgbUnit(v)), + }; + const rgba = { + test: /*@__PURE__*/ isColorString("rgb", "red"), + parse: /*@__PURE__*/ splitColor("red", "green", "blue"), + transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + + rgbUnit.transform(red) + + ", " + + rgbUnit.transform(green) + + ", " + + rgbUnit.transform(blue) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")", + }; + + function parseHex(v) { + let r = ""; + let g = ""; + let b = ""; + let a = ""; + // If we have 6 characters, ie #FF0000 + if (v.length > 5) { + r = v.substring(1, 3); + g = v.substring(3, 5); + b = v.substring(5, 7); + a = v.substring(7, 9); + // Or we have 3 characters, ie #F00 + } + else { + r = v.substring(1, 2); + g = v.substring(2, 3); + b = v.substring(3, 4); + a = v.substring(4, 5); + r += r; + g += g; + b += b; + a += a; + } + return { + red: parseInt(r, 16), + green: parseInt(g, 16), + blue: parseInt(b, 16), + alpha: a ? parseInt(a, 16) / 255 : 1, + }; + } + const hex = { + test: /*@__PURE__*/ isColorString("#"), + parse: parseHex, + transform: rgba.transform, + }; + + /*#__NO_SIDE_EFFECTS__*/ + const createUnitType = (unit) => ({ + test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1, + parse: parseFloat, + transform: (v) => `${v}${unit}`, + }); + const degrees = /*@__PURE__*/ createUnitType("deg"); + const percent = /*@__PURE__*/ createUnitType("%"); + const px = /*@__PURE__*/ createUnitType("px"); + const vh = /*@__PURE__*/ createUnitType("vh"); + const vw = /*@__PURE__*/ createUnitType("vw"); + const progressPercentage = /*@__PURE__*/ (() => ({ + ...percent, + parse: (v) => percent.parse(v) / 100, + transform: (v) => percent.transform(v * 100), + }))(); + + const hsla = { + test: /*@__PURE__*/ isColorString("hsl", "hue"), + parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"), + transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { + return ("hsla(" + + Math.round(hue) + + ", " + + percent.transform(sanitize(saturation)) + + ", " + + percent.transform(sanitize(lightness)) + + ", " + + sanitize(alpha.transform(alpha$1)) + + ")"); + }, + }; + + const color = { + test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v), + parse: (v) => { + if (rgba.test(v)) { + return rgba.parse(v); + } + else if (hsla.test(v)) { + return hsla.parse(v); + } + else { + return hex.parse(v); + } + }, + transform: (v) => { + return typeof v === "string" + ? v + : v.hasOwnProperty("red") + ? rgba.transform(v) + : hsla.transform(v); + }, + getAnimatableNone: (v) => { + const parsed = color.parse(v); + parsed.alpha = 0; + return color.transform(parsed); + }, + }; + + const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu; + + function test(v) { + return (isNaN(v) && + typeof v === "string" && + (v.match(floatRegex)?.length || 0) + + (v.match(colorRegex)?.length || 0) > + 0); + } + const NUMBER_TOKEN = "number"; + const COLOR_TOKEN = "color"; + const VAR_TOKEN = "var"; + const VAR_FUNCTION_TOKEN = "var("; + const SPLIT_TOKEN = "${}"; + // this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex` + const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu; + function analyseComplexValue(value) { + const originalValue = value.toString(); + const values = []; + const indexes = { + color: [], + number: [], + var: [], + }; + const types = []; + let i = 0; + const tokenised = originalValue.replace(complexRegex, (parsedValue) => { + if (color.test(parsedValue)) { + indexes.color.push(i); + types.push(COLOR_TOKEN); + values.push(color.parse(parsedValue)); + } + else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) { + indexes.var.push(i); + types.push(VAR_TOKEN); + values.push(parsedValue); + } + else { + indexes.number.push(i); + types.push(NUMBER_TOKEN); + values.push(parseFloat(parsedValue)); + } + ++i; + return SPLIT_TOKEN; + }); + const split = tokenised.split(SPLIT_TOKEN); + return { values, split, indexes, types }; + } + function parseComplexValue(v) { + return analyseComplexValue(v).values; + } + function createTransformer(source) { + const { split, types } = analyseComplexValue(source); + const numSections = split.length; + return (v) => { + let output = ""; + for (let i = 0; i < numSections; i++) { + output += split[i]; + if (v[i] !== undefined) { + const type = types[i]; + if (type === NUMBER_TOKEN) { + output += sanitize(v[i]); + } + else if (type === COLOR_TOKEN) { + output += color.transform(v[i]); + } + else { + output += v[i]; + } + } + } + return output; + }; + } + const convertNumbersToZero = (v) => typeof v === "number" ? 0 : color.test(v) ? color.getAnimatableNone(v) : v; + function getAnimatableNone$1(v) { + const parsed = parseComplexValue(v); + const transformer = createTransformer(v); + return transformer(parsed.map(convertNumbersToZero)); + } + const complex = { + test, + parse: parseComplexValue, + createTransformer, + getAnimatableNone: getAnimatableNone$1, + }; + + // Adapted from https://gist.github.com/mjackson/5311256 + function hueToRgb(p, q, t) { + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < 1 / 6) + return p + (q - p) * 6 * t; + if (t < 1 / 2) + return q; + if (t < 2 / 3) + return p + (q - p) * (2 / 3 - t) * 6; + return p; + } + function hslaToRgba({ hue, saturation, lightness, alpha }) { + hue /= 360; + saturation /= 100; + lightness /= 100; + let red = 0; + let green = 0; + let blue = 0; + if (!saturation) { + red = green = blue = lightness; + } + else { + const q = lightness < 0.5 + ? lightness * (1 + saturation) + : lightness + saturation - lightness * saturation; + const p = 2 * lightness - q; + red = hueToRgb(p, q, hue + 1 / 3); + green = hueToRgb(p, q, hue); + blue = hueToRgb(p, q, hue - 1 / 3); + } + return { + red: Math.round(red * 255), + green: Math.round(green * 255), + blue: Math.round(blue * 255), + alpha, + }; + } + + function mixImmediate(a, b) { + return (p) => (p > 0 ? b : a); + } + + /* + Value in range from progress + + Given a lower limit and an upper limit, we return the value within + that range as expressed by progress (usually a number from 0 to 1) + + So progress = 0.5 would change + + from -------- to + + to + + from ---- to + + E.g. from = 10, to = 20, progress = 0.5 => 15 + + @param [number]: Lower limit of range + @param [number]: Upper limit of range + @param [number]: The progress between lower and upper limits expressed 0-1 + @return [number]: Value as calculated from progress within range (not limited within range) + */ + const mixNumber$1 = (from, to, progress) => { + return from + (to - from) * progress; + }; + + // Linear color space blending + // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw + // Demonstrated http://codepen.io/osublake/pen/xGVVaN + const mixLinearColor = (from, to, v) => { + const fromExpo = from * from; + const expo = v * (to * to - fromExpo) + fromExpo; + return expo < 0 ? 0 : Math.sqrt(expo); + }; + const colorTypes = [hex, rgba, hsla]; + const getColorType = (v) => colorTypes.find((type) => type.test(v)); + function asRGBA(color) { + const type = getColorType(color); + motionUtils.warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`); + if (!Boolean(type)) + return false; + let model = type.parse(color); + if (type === hsla) { + // TODO Remove this cast - needed since Motion's stricter typing + model = hslaToRgba(model); + } + return model; + } + const mixColor = (from, to) => { + const fromRGBA = asRGBA(from); + const toRGBA = asRGBA(to); + if (!fromRGBA || !toRGBA) { + return mixImmediate(from, to); + } + const blended = { ...fromRGBA }; + return (v) => { + blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v); + blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v); + blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v); + blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v); + return rgba.transform(blended); + }; + }; + + const invisibleValues = new Set(["none", "hidden"]); + /** + * Returns a function that, when provided a progress value between 0 and 1, + * will return the "none" or "hidden" string only when the progress is that of + * the origin or target. + */ + function mixVisibility(origin, target) { + if (invisibleValues.has(origin)) { + return (p) => (p <= 0 ? origin : target); + } + else { + return (p) => (p >= 1 ? target : origin); + } + } + + function mixNumber(a, b) { + return (p) => mixNumber$1(a, b, p); + } + function getMixer(a) { + if (typeof a === "number") { + return mixNumber; + } + else if (typeof a === "string") { + return isCSSVariableToken(a) + ? mixImmediate + : color.test(a) + ? mixColor + : mixComplex; + } + else if (Array.isArray(a)) { + return mixArray; + } + else if (typeof a === "object") { + return color.test(a) ? mixColor : mixObject; + } + return mixImmediate; + } + function mixArray(a, b) { + const output = [...a]; + const numValues = output.length; + const blendValue = a.map((v, i) => getMixer(v)(v, b[i])); + return (p) => { + for (let i = 0; i < numValues; i++) { + output[i] = blendValue[i](p); + } + return output; + }; + } + function mixObject(a, b) { + const output = { ...a, ...b }; + const blendValue = {}; + for (const key in output) { + if (a[key] !== undefined && b[key] !== undefined) { + blendValue[key] = getMixer(a[key])(a[key], b[key]); + } + } + return (v) => { + for (const key in blendValue) { + output[key] = blendValue[key](v); + } + return output; + }; + } + function matchOrder(origin, target) { + const orderedOrigin = []; + const pointers = { color: 0, var: 0, number: 0 }; + for (let i = 0; i < target.values.length; i++) { + const type = target.types[i]; + const originIndex = origin.indexes[type][pointers[type]]; + const originValue = origin.values[originIndex] ?? 0; + orderedOrigin[i] = originValue; + pointers[type]++; + } + return orderedOrigin; + } + const mixComplex = (origin, target) => { + const template = complex.createTransformer(target); + const originStats = analyseComplexValue(origin); + const targetStats = analyseComplexValue(target); + const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length && + originStats.indexes.color.length === targetStats.indexes.color.length && + originStats.indexes.number.length >= targetStats.indexes.number.length; + if (canInterpolate) { + if ((invisibleValues.has(origin) && + !targetStats.values.length) || + (invisibleValues.has(target) && + !originStats.values.length)) { + return mixVisibility(origin, target); + } + return motionUtils.pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template); + } + else { + motionUtils.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`); + return mixImmediate(origin, target); + } + }; + + function mix(from, to, p) { + if (typeof from === "number" && + typeof to === "number" && + typeof p === "number") { + return mixNumber$1(from, to, p); + } + const mixer = getMixer(from); + return mixer(from, to); + } + + const frameloopDriver = (update) => { + const passTimestamp = ({ timestamp }) => update(timestamp); + return { + start: (keepAlive = true) => frame.update(passTimestamp, keepAlive), + stop: () => cancelFrame(passTimestamp), + /** + * If we're processing this frame we can use the + * framelocked timestamp to keep things in sync. + */ + now: () => (frameData.isProcessing ? frameData.timestamp : time.now()), + }; + }; + + const generateLinearEasing = (easing, duration, // as milliseconds + resolution = 10 // as milliseconds + ) => { + let points = ""; + const numPoints = Math.max(Math.round(duration / resolution), 2); + for (let i = 0; i < numPoints; i++) { + points += Math.round(easing(i / (numPoints - 1)) * 10000) / 10000 + ", "; + } + return `linear(${points.substring(0, points.length - 2)})`; + }; + + /** + * Implement a practical max duration for keyframe generation + * to prevent infinite loops + */ + const maxGeneratorDuration = 20000; + function calcGeneratorDuration(generator) { + let duration = 0; + const timeStep = 50; + let state = generator.next(duration); + while (!state.done && duration < maxGeneratorDuration) { + duration += timeStep; + state = generator.next(duration); + } + return duration >= maxGeneratorDuration ? Infinity : duration; + } + + /** + * Create a progress => progress easing function from a generator. + */ + function createGeneratorEasing(options, scale = 100, createGenerator) { + const generator = createGenerator({ ...options, keyframes: [0, scale] }); + const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + return { + type: "keyframes", + ease: (progress) => { + return generator.next(duration * progress).value / scale; + }, + duration: motionUtils.millisecondsToSeconds(duration), + }; + } + + const velocitySampleDuration = 5; // ms + function calcGeneratorVelocity(resolveValue, t, current) { + const prevT = Math.max(t - velocitySampleDuration, 0); + return motionUtils.velocityPerSecond(current - resolveValue(prevT), t - prevT); + } + + const springDefaults = { + // Default spring physics + stiffness: 100, + damping: 10, + mass: 1.0, + velocity: 0.0, + // Default duration/bounce-based options + duration: 800, // in ms + bounce: 0.3, + visualDuration: 0.3, // in seconds + // Rest thresholds + restSpeed: { + granular: 0.01, + default: 2, + }, + restDelta: { + granular: 0.005, + default: 0.5, + }, + // Limits + minDuration: 0.01, // in seconds + maxDuration: 10.0, // in seconds + minDamping: 0.05, + maxDamping: 1, + }; + + const safeMin = 0.001; + function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) { + let envelope; + let derivative; + motionUtils.warning(duration <= motionUtils.secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less"); + let dampingRatio = 1 - bounce; + /** + * Restrict dampingRatio and duration to within acceptable ranges. + */ + dampingRatio = motionUtils.clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio); + duration = motionUtils.clamp(springDefaults.minDuration, springDefaults.maxDuration, motionUtils.millisecondsToSeconds(duration)); + if (dampingRatio < 1) { + /** + * Underdamped spring + */ + envelope = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const a = exponentialDecay - velocity; + const b = calcAngularFreq(undampedFreq, dampingRatio); + const c = Math.exp(-delta); + return safeMin - (a / b) * c; + }; + derivative = (undampedFreq) => { + const exponentialDecay = undampedFreq * dampingRatio; + const delta = exponentialDecay * duration; + const d = delta * velocity + velocity; + const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration; + const f = Math.exp(-delta); + const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio); + const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1; + return (factor * ((d - e) * f)) / g; + }; + } + else { + /** + * Critically-damped spring + */ + envelope = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (undampedFreq - velocity) * duration + 1; + return -safeMin + a * b; + }; + derivative = (undampedFreq) => { + const a = Math.exp(-undampedFreq * duration); + const b = (velocity - undampedFreq) * (duration * duration); + return a * b; + }; + } + const initialGuess = 5 / duration; + const undampedFreq = approximateRoot(envelope, derivative, initialGuess); + duration = motionUtils.secondsToMilliseconds(duration); + if (isNaN(undampedFreq)) { + return { + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + duration, + }; + } + else { + const stiffness = Math.pow(undampedFreq, 2) * mass; + return { + stiffness, + damping: dampingRatio * 2 * Math.sqrt(mass * stiffness), + duration, + }; + } + } + const rootIterations = 12; + function approximateRoot(envelope, derivative, initialGuess) { + let result = initialGuess; + for (let i = 1; i < rootIterations; i++) { + result = result - envelope(result) / derivative(result); + } + return result; + } + function calcAngularFreq(undampedFreq, dampingRatio) { + return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio); + } + + const durationKeys = ["duration", "bounce"]; + const physicsKeys = ["stiffness", "damping", "mass"]; + function isSpringType(options, keys) { + return keys.some((key) => options[key] !== undefined); + } + function getSpringOptions(options) { + let springOptions = { + velocity: springDefaults.velocity, + stiffness: springDefaults.stiffness, + damping: springDefaults.damping, + mass: springDefaults.mass, + isResolvedFromDuration: false, + ...options, + }; + // stiffness/damping/mass overrides duration/bounce + if (!isSpringType(options, physicsKeys) && + isSpringType(options, durationKeys)) { + if (options.visualDuration) { + const visualDuration = options.visualDuration; + const root = (2 * Math.PI) / (visualDuration * 1.2); + const stiffness = root * root; + const damping = 2 * + motionUtils.clamp(0.05, 1, 1 - (options.bounce || 0)) * + Math.sqrt(stiffness); + springOptions = { + ...springOptions, + mass: springDefaults.mass, + stiffness, + damping, + }; + } + else { + const derived = findSpring(options); + springOptions = { + ...springOptions, + ...derived, + mass: springDefaults.mass, + }; + springOptions.isResolvedFromDuration = true; + } + } + return springOptions; + } + function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) { + const options = typeof optionsOrVisualDuration !== "object" + ? { + visualDuration: optionsOrVisualDuration, + keyframes: [0, 1], + bounce, + } + : optionsOrVisualDuration; + let { restSpeed, restDelta } = options; + const origin = options.keyframes[0]; + const target = options.keyframes[options.keyframes.length - 1]; + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { done: false, value: origin }; + const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({ + ...options, + velocity: -motionUtils.millisecondsToSeconds(options.velocity || 0), + }); + const initialVelocity = velocity || 0.0; + const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass)); + const initialDelta = target - origin; + const undampedAngularFreq = motionUtils.millisecondsToSeconds(Math.sqrt(stiffness / mass)); + /** + * If we're working on a granular scale, use smaller defaults for determining + * when the spring is finished. + * + * These defaults have been selected emprically based on what strikes a good + * ratio between feeling good and finishing as soon as changes are imperceptible. + */ + const isGranularScale = Math.abs(initialDelta) < 5; + restSpeed || (restSpeed = isGranularScale + ? springDefaults.restSpeed.granular + : springDefaults.restSpeed.default); + restDelta || (restDelta = isGranularScale + ? springDefaults.restDelta.granular + : springDefaults.restDelta.default); + let resolveSpring; + if (dampingRatio < 1) { + const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio); + // Underdamped spring + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + return (target - + envelope * + (((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) / + angularFreq) * + Math.sin(angularFreq * t) + + initialDelta * Math.cos(angularFreq * t))); + }; + } + else if (dampingRatio === 1) { + // Critically damped spring + resolveSpring = (t) => target - + Math.exp(-undampedAngularFreq * t) * + (initialDelta + + (initialVelocity + undampedAngularFreq * initialDelta) * t); + } + else { + // Overdamped spring + const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1); + resolveSpring = (t) => { + const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); + // When performing sinh or cosh values can hit Infinity so we cap them here + const freqForT = Math.min(dampedAngularFreq * t, 300); + return (target - + (envelope * + ((initialVelocity + + dampingRatio * undampedAngularFreq * initialDelta) * + Math.sinh(freqForT) + + dampedAngularFreq * + initialDelta * + Math.cosh(freqForT))) / + dampedAngularFreq); + }; + } + const generator = { + calculatedDuration: isResolvedFromDuration ? duration || null : null, + next: (t) => { + const current = resolveSpring(t); + if (!isResolvedFromDuration) { + let currentVelocity = t === 0 ? initialVelocity : 0.0; + /** + * We only need to calculate velocity for under-damped springs + * as over- and critically-damped springs can't overshoot, so + * checking only for displacement is enough. + */ + if (dampingRatio < 1) { + currentVelocity = + t === 0 + ? motionUtils.secondsToMilliseconds(initialVelocity) + : calcGeneratorVelocity(resolveSpring, t, current); + } + const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed; + const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta; + state.done = + isBelowVelocityThreshold && isBelowDisplacementThreshold; + } + else { + state.done = t >= duration; + } + state.value = state.done ? target : current; + return state; + }, + toString: () => { + const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); + const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30); + return calculatedDuration + "ms " + easing; + }, + toTransition: () => { }, + }; + return generator; + } + spring.applyToOptions = (options) => { + const generatorOptions = createGeneratorEasing(options, 100, spring); + options.ease = generatorOptions.ease; + options.duration = motionUtils.secondsToMilliseconds(generatorOptions.duration); + options.type = "keyframes"; + return options; + }; + + function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) { + const origin = keyframes[0]; + const state = { + done: false, + value: origin, + }; + const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max); + const nearestBoundary = (v) => { + if (min === undefined) + return max; + if (max === undefined) + return min; + return Math.abs(min - v) < Math.abs(max - v) ? min : max; + }; + let amplitude = power * velocity; + const ideal = origin + amplitude; + const target = modifyTarget === undefined ? ideal : modifyTarget(ideal); + /** + * If the target has changed we need to re-calculate the amplitude, otherwise + * the animation will start from the wrong position. + */ + if (target !== ideal) + amplitude = target - origin; + const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant); + const calcLatest = (t) => target + calcDelta(t); + const applyFriction = (t) => { + const delta = calcDelta(t); + const latest = calcLatest(t); + state.done = Math.abs(delta) <= restDelta; + state.value = state.done ? target : latest; + }; + /** + * Ideally this would resolve for t in a stateless way, we could + * do that by always precalculating the animation but as we know + * this will be done anyway we can assume that spring will + * be discovered during that. + */ + let timeReachedBoundary; + let spring$1; + const checkCatchBoundary = (t) => { + if (!isOutOfBounds(state.value)) + return; + timeReachedBoundary = t; + spring$1 = spring({ + keyframes: [state.value, nearestBoundary(state.value)], + velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000 + damping: bounceDamping, + stiffness: bounceStiffness, + restDelta, + restSpeed, + }); + }; + checkCatchBoundary(0); + return { + calculatedDuration: null, + next: (t) => { + /** + * We need to resolve the friction to figure out if we need a + * spring but we don't want to do this twice per frame. So here + * we flag if we updated for this frame and later if we did + * we can skip doing it again. + */ + let hasUpdatedFrame = false; + if (!spring$1 && timeReachedBoundary === undefined) { + hasUpdatedFrame = true; + applyFriction(t); + checkCatchBoundary(t); + } + /** + * If we have a spring and the provided t is beyond the moment the friction + * animation crossed the min/max boundary, use the spring. + */ + if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) { + return spring$1.next(t - timeReachedBoundary); + } + else { + !hasUpdatedFrame && applyFriction(t); + return state; + } + }, + }; + } + + function createMixers(output, ease, customMixer) { + const mixers = []; + const mixerFactory = customMixer || motionUtils.MotionGlobalConfig.mix || mix; + const numMixers = output.length - 1; + for (let i = 0; i < numMixers; i++) { + let mixer = mixerFactory(output[i], output[i + 1]); + if (ease) { + const easingFunction = Array.isArray(ease) ? ease[i] || motionUtils.noop : ease; + mixer = motionUtils.pipe(easingFunction, mixer); + } + mixers.push(mixer); + } + return mixers; + } + /** + * Create a function that maps from a numerical input array to a generic output array. + * + * Accepts: + * - Numbers + * - Colors (hex, hsl, hsla, rgb, rgba) + * - Complex (combinations of one or more numbers or strings) + * + * ```jsx + * const mixColor = interpolate([0, 1], ['#fff', '#000']) + * + * mixColor(0.5) // 'rgba(128, 128, 128, 1)' + * ``` + * + * TODO Revisit this approach once we've moved to data models for values, + * probably not needed to pregenerate mixer functions. + * + * @public + */ + function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) { + const inputLength = input.length; + motionUtils.invariant(inputLength === output.length, "Both input and output ranges must be the same length"); + /** + * If we're only provided a single input, we can just make a function + * that returns the output. + */ + if (inputLength === 1) + return () => output[0]; + if (inputLength === 2 && output[0] === output[1]) + return () => output[1]; + const isZeroDeltaRange = input[0] === input[1]; + // If input runs highest -> lowest, reverse both arrays + if (input[0] > input[inputLength - 1]) { + input = [...input].reverse(); + output = [...output].reverse(); + } + const mixers = createMixers(output, ease, mixer); + const numMixers = mixers.length; + const interpolator = (v) => { + if (isZeroDeltaRange && v < input[0]) + return output[0]; + let i = 0; + if (numMixers > 1) { + for (; i < input.length - 2; i++) { + if (v < input[i + 1]) + break; + } + } + const progressInRange = motionUtils.progress(input[i], input[i + 1], v); + return mixers[i](progressInRange); + }; + return isClamp + ? (v) => interpolator(motionUtils.clamp(input[0], input[inputLength - 1], v)) + : interpolator; + } + + function fillOffset(offset, remaining) { + const min = offset[offset.length - 1]; + for (let i = 1; i <= remaining; i++) { + const offsetProgress = motionUtils.progress(0, remaining, i); + offset.push(mixNumber$1(min, 1, offsetProgress)); + } + } + + function defaultOffset(arr) { + const offset = [0]; + fillOffset(offset, arr.length - 1); + return offset; + } + + function convertOffsetToTimes(offset, duration) { + return offset.map((o) => o * duration); + } + + function defaultEasing(values, easing) { + return values.map(() => easing || motionUtils.easeInOut).splice(0, values.length - 1); + } + function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) { + /** + * Easing functions can be externally defined as strings. Here we convert them + * into actual functions. + */ + const easingFunctions = motionUtils.isEasingArray(ease) + ? ease.map(motionUtils.easingDefinitionToFunction) + : motionUtils.easingDefinitionToFunction(ease); + /** + * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator + * to reduce GC during animation. + */ + const state = { + done: false, + value: keyframeValues[0], + }; + /** + * Create a times array based on the provided 0-1 offsets + */ + const absoluteTimes = convertOffsetToTimes( + // Only use the provided offsets if they're the correct length + // TODO Maybe we should warn here if there's a length mismatch + times && times.length === keyframeValues.length + ? times + : defaultOffset(keyframeValues), duration); + const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, { + ease: Array.isArray(easingFunctions) + ? easingFunctions + : defaultEasing(keyframeValues, easingFunctions), + }); + return { + calculatedDuration: duration, + next: (t) => { + state.value = mapTimeToKeyframe(t); + state.done = t >= duration; + return state; + }, + }; + } + + const isNotNull = (value) => value !== null; + function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) { + const resolvedKeyframes = keyframes.filter(isNotNull); + const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1); + const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1; + return !index || finalKeyframe === undefined + ? resolvedKeyframes[index] + : finalKeyframe; + } + + const transitionTypeMap = { + decay: inertia, + inertia, + tween: keyframes, + keyframes: keyframes, + spring, + }; + function replaceTransitionType(transition) { + if (typeof transition.type === "string") { + transition.type = transitionTypeMap[transition.type]; + } + } + + class WithPromise { + constructor() { + this.updateFinished(); + } + get finished() { + return this._finished; + } + updateFinished() { + this._finished = new Promise((resolve) => { + this.resolve = resolve; + }); + } + notifyFinished() { + this.resolve(); + } + /** + * Allows the animation to be awaited. + * + * @deprecated Use `finished` instead. + */ + then(onResolve, onReject) { + return this.finished.then(onResolve, onReject); + } + } + + const percentToProgress = (percent) => percent / 100; + class JSAnimation extends WithPromise { + constructor(options) { + super(); + this.state = "idle"; + this.startTime = null; + this.isStopped = false; + /** + * The current time of the animation. + */ + this.currentTime = 0; + /** + * The time at which the animation was paused. + */ + this.holdTime = null; + /** + * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed. + */ + this.playbackSpeed = 1; + /** + * This method is bound to the instance to fix a pattern where + * animation.stop is returned as a reference from a useEffect. + */ + this.stop = () => { + const { motionValue } = this.options; + if (motionValue && motionValue.updatedAt !== time.now()) { + this.tick(time.now()); + } + this.isStopped = true; + if (this.state === "idle") + return; + this.teardown(); + this.options.onStop?.(); + }; + activeAnimations.mainThread++; + this.options = options; + this.initAnimation(); + this.play(); + if (options.autoplay === false) + this.pause(); + } + initAnimation() { + const { options } = this; + replaceTransitionType(options); + const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options; + let { keyframes: keyframes$1 } = options; + const generatorFactory = type || keyframes; + if (generatorFactory !== keyframes) { + motionUtils.invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`); + } + if (generatorFactory !== keyframes && + typeof keyframes$1[0] !== "number") { + this.mixKeyframes = motionUtils.pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1])); + keyframes$1 = [0, 100]; + } + const generator = generatorFactory({ ...options, keyframes: keyframes$1 }); + /** + * If we have a mirror repeat type we need to create a second generator that outputs the + * mirrored (not reversed) animation and later ping pong between the two generators. + */ + if (repeatType === "mirror") { + this.mirroredGenerator = generatorFactory({ + ...options, + keyframes: [...keyframes$1].reverse(), + velocity: -velocity, + }); + } + /** + * If duration is undefined and we have repeat options, + * we need to calculate a duration from the generator. + * + * We set it to the generator itself to cache the duration. + * Any timeline resolver will need to have already precalculated + * the duration by this step. + */ + if (generator.calculatedDuration === null) { + generator.calculatedDuration = calcGeneratorDuration(generator); + } + const { calculatedDuration } = generator; + this.calculatedDuration = calculatedDuration; + this.resolvedDuration = calculatedDuration + repeatDelay; + this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay; + this.generator = generator; + } + updateTime(timestamp) { + const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed; + // Update currentTime + if (this.holdTime !== null) { + this.currentTime = this.holdTime; + } + else { + // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 = + // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for + // example. + this.currentTime = animationTime; + } + } + tick(timestamp, sample = false) { + const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this; + if (this.startTime === null) + return generator.next(0); + const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options; + /** + * requestAnimationFrame timestamps can come through as lower than + * the startTime as set by performance.now(). Here we prevent this, + * though in the future it could be possible to make setting startTime + * a pending operation that gets resolved here. + */ + if (this.speed > 0) { + this.startTime = Math.min(this.startTime, timestamp); + } + else if (this.speed < 0) { + this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime); + } + if (sample) { + this.currentTime = timestamp; + } + else { + this.updateTime(timestamp); + } + // Rebase on delay + const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1); + const isInDelayPhase = this.playbackSpeed >= 0 + ? timeWithoutDelay < 0 + : timeWithoutDelay > totalDuration; + this.currentTime = Math.max(timeWithoutDelay, 0); + // If this animation has finished, set the current time to the total duration. + if (this.state === "finished" && this.holdTime === null) { + this.currentTime = totalDuration; + } + let elapsed = this.currentTime; + let frameGenerator = generator; + if (repeat) { + /** + * Get the current progress (0-1) of the animation. If t is > + * than duration we'll get values like 2.5 (midway through the + * third iteration) + */ + const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration; + /** + * Get the current iteration (0 indexed). For instance the floor of + * 2.5 is 2. + */ + let currentIteration = Math.floor(progress); + /** + * Get the current progress of the iteration by taking the remainder + * so 2.5 is 0.5 through iteration 2 + */ + let iterationProgress = progress % 1.0; + /** + * If iteration progress is 1 we count that as the end + * of the previous iteration. + */ + if (!iterationProgress && progress >= 1) { + iterationProgress = 1; + } + iterationProgress === 1 && currentIteration--; + currentIteration = Math.min(currentIteration, repeat + 1); + /** + * Reverse progress if we're not running in "normal" direction + */ + const isOddIteration = Boolean(currentIteration % 2); + if (isOddIteration) { + if (repeatType === "reverse") { + iterationProgress = 1 - iterationProgress; + if (repeatDelay) { + iterationProgress -= repeatDelay / resolvedDuration; + } + } + else if (repeatType === "mirror") { + frameGenerator = mirroredGenerator; + } + } + elapsed = motionUtils.clamp(0, 1, iterationProgress) * resolvedDuration; + } + /** + * If we're in negative time, set state as the initial keyframe. + * This prevents delay: x, duration: 0 animations from finishing + * instantly. + */ + const state = isInDelayPhase + ? { done: false, value: keyframes[0] } + : frameGenerator.next(elapsed); + if (mixKeyframes) { + state.value = mixKeyframes(state.value); + } + let { done } = state; + if (!isInDelayPhase && calculatedDuration !== null) { + done = + this.playbackSpeed >= 0 + ? this.currentTime >= totalDuration + : this.currentTime <= 0; + } + const isAnimationFinished = this.holdTime === null && + (this.state === "finished" || (this.state === "running" && done)); + // TODO: The exception for inertia could be cleaner here + if (isAnimationFinished && type !== inertia) { + state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed); + } + if (onUpdate) { + onUpdate(state.value); + } + if (isAnimationFinished) { + this.finish(); + } + return state; + } + /** + * Allows the returned animation to be awaited or promise-chained. Currently + * resolves when the animation finishes at all but in a future update could/should + * reject if its cancels. + */ + then(resolve, reject) { + return this.finished.then(resolve, reject); + } + get duration() { + return motionUtils.millisecondsToSeconds(this.calculatedDuration); + } + get time() { + return motionUtils.millisecondsToSeconds(this.currentTime); + } + set time(newTime) { + newTime = motionUtils.secondsToMilliseconds(newTime); + this.currentTime = newTime; + if (this.startTime === null || + this.holdTime !== null || + this.playbackSpeed === 0) { + this.holdTime = newTime; + } + else if (this.driver) { + this.startTime = this.driver.now() - newTime / this.playbackSpeed; + } + this.driver?.start(false); + } + get speed() { + return this.playbackSpeed; + } + set speed(newSpeed) { + this.updateTime(time.now()); + const hasChanged = this.playbackSpeed !== newSpeed; + this.playbackSpeed = newSpeed; + if (hasChanged) { + this.time = motionUtils.millisecondsToSeconds(this.currentTime); + } + } + play() { + if (this.isStopped) + return; + const { driver = frameloopDriver, startTime } = this.options; + if (!this.driver) { + this.driver = driver((timestamp) => this.tick(timestamp)); + } + this.options.onPlay?.(); + const now = this.driver.now(); + if (this.state === "finished") { + this.updateFinished(); + this.startTime = now; + } + else if (this.holdTime !== null) { + this.startTime = now - this.holdTime; + } + else if (!this.startTime) { + this.startTime = startTime ?? now; + } + if (this.state === "finished" && this.speed < 0) { + this.startTime += this.calculatedDuration; + } + this.holdTime = null; + /** + * Set playState to running only after we've used it in + * the previous logic. + */ + this.state = "running"; + this.driver.start(); + } + pause() { + this.state = "paused"; + this.updateTime(time.now()); + this.holdTime = this.currentTime; + } + complete() { + if (this.state !== "running") { + this.play(); + } + this.state = "finished"; + this.holdTime = null; + } + finish() { + this.notifyFinished(); + this.teardown(); + this.state = "finished"; + this.options.onComplete?.(); + } + cancel() { + this.holdTime = null; + this.startTime = 0; + this.tick(0); + this.teardown(); + this.options.onCancel?.(); + } + teardown() { + this.state = "idle"; + this.stopDriver(); + this.startTime = this.holdTime = null; + activeAnimations.mainThread--; + } + stopDriver() { + if (!this.driver) + return; + this.driver.stop(); + this.driver = undefined; + } + sample(sampleTime) { + this.startTime = 0; + return this.tick(sampleTime, true); + } + attachTimeline(timeline) { + if (this.options.allowFlatten) { + this.options.type = "keyframes"; + this.options.ease = "linear"; + this.initAnimation(); + } + this.driver?.stop(); + return timeline.observe(this); + } + } + // Legacy function support + function animateValue(options) { + return new JSAnimation(options); + } + + function fillWildcards(keyframes) { + for (let i = 1; i < keyframes.length; i++) { + keyframes[i] ?? (keyframes[i] = keyframes[i - 1]); + } + } + + const radToDeg = (rad) => (rad * 180) / Math.PI; + const rotate = (v) => { + const angle = radToDeg(Math.atan2(v[1], v[0])); + return rebaseAngle(angle); + }; + const matrix2dParsers = { + x: 4, + y: 5, + translateX: 4, + translateY: 5, + scaleX: 0, + scaleY: 3, + scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2, + rotate, + rotateZ: rotate, + skewX: (v) => radToDeg(Math.atan(v[1])), + skewY: (v) => radToDeg(Math.atan(v[2])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2, + }; + const rebaseAngle = (angle) => { + angle = angle % 360; + if (angle < 0) + angle += 360; + return angle; + }; + const rotateZ = rotate; + const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]); + const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]); + const matrix3dParsers = { + x: 12, + y: 13, + z: 14, + translateX: 12, + translateY: 13, + translateZ: 14, + scaleX, + scaleY, + scale: (v) => (scaleX(v) + scaleY(v)) / 2, + rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))), + rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))), + rotateZ, + rotate: rotateZ, + skewX: (v) => radToDeg(Math.atan(v[4])), + skewY: (v) => radToDeg(Math.atan(v[1])), + skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2, + }; + function defaultTransformValue(name) { + return name.includes("scale") ? 1 : 0; + } + function parseValueFromTransform(transform, name) { + if (!transform || transform === "none") { + return defaultTransformValue(name); + } + const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u); + let parsers; + let match; + if (matrix3dMatch) { + parsers = matrix3dParsers; + match = matrix3dMatch; + } + else { + const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u); + parsers = matrix2dParsers; + match = matrix2dMatch; + } + if (!match) { + return defaultTransformValue(name); + } + const valueParser = parsers[name]; + const values = match[1].split(",").map(convertTransformToNumber); + return typeof valueParser === "function" + ? valueParser(values) + : values[valueParser]; + } + const readTransformValue = (instance, name) => { + const { transform = "none" } = getComputedStyle(instance); + return parseValueFromTransform(transform, name); + }; + function convertTransformToNumber(value) { + return parseFloat(value.trim()); + } + + /** + * Generate a list of every possible transform key. + */ + const transformPropOrder = [ + "transformPerspective", + "x", + "y", + "z", + "translateX", + "translateY", + "translateZ", + "scale", + "scaleX", + "scaleY", + "rotate", + "rotateX", + "rotateY", + "rotateZ", + "skew", + "skewX", + "skewY", + ]; + /** + * A quick lookup for transform props. + */ + const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))(); + + const isNumOrPxType = (v) => v === number || v === px; + const transformKeys = new Set(["x", "y", "z"]); + const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key)); + function removeNonTranslationalTransform(visualElement) { + const removedTransforms = []; + nonTranslationalTransformKeys.forEach((key) => { + const value = visualElement.getValue(key); + if (value !== undefined) { + removedTransforms.push([key, value.get()]); + value.set(key.startsWith("scale") ? 1 : 0); + } + }); + return removedTransforms; + } + const positionalValues = { + // Dimensions + width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight), + height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom), + top: (_bbox, { top }) => parseFloat(top), + left: (_bbox, { left }) => parseFloat(left), + bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min), + right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min), + // Transform + x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"), + y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"), + }; + // Alias translate longform names + positionalValues.translateX = positionalValues.x; + positionalValues.translateY = positionalValues.y; + + const toResolve = new Set(); + let isScheduled = false; + let anyNeedsMeasurement = false; + let isForced = false; + function measureAllKeyframes() { + if (anyNeedsMeasurement) { + const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement); + const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element)); + const transformsToRestore = new Map(); + /** + * Write pass + * If we're measuring elements we want to remove bounding box-changing transforms. + */ + elementsToMeasure.forEach((element) => { + const removedTransforms = removeNonTranslationalTransform(element); + if (!removedTransforms.length) + return; + transformsToRestore.set(element, removedTransforms); + element.render(); + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureInitialState()); + // Write + elementsToMeasure.forEach((element) => { + element.render(); + const restore = transformsToRestore.get(element); + if (restore) { + restore.forEach(([key, value]) => { + element.getValue(key)?.set(value); + }); + } + }); + // Read + resolversToMeasure.forEach((resolver) => resolver.measureEndState()); + // Write + resolversToMeasure.forEach((resolver) => { + if (resolver.suspendedScrollY !== undefined) { + window.scrollTo(0, resolver.suspendedScrollY); + } + }); + } + anyNeedsMeasurement = false; + isScheduled = false; + toResolve.forEach((resolver) => resolver.complete(isForced)); + toResolve.clear(); + } + function readAllKeyframes() { + toResolve.forEach((resolver) => { + resolver.readKeyframes(); + if (resolver.needsMeasurement) { + anyNeedsMeasurement = true; + } + }); + } + function flushKeyframeResolvers() { + isForced = true; + readAllKeyframes(); + measureAllKeyframes(); + isForced = false; + } + class KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) { + this.state = "pending"; + /** + * Track whether this resolver is async. If it is, it'll be added to the + * resolver queue and flushed in the next frame. Resolvers that aren't going + * to trigger read/write thrashing don't need to be async. + */ + this.isAsync = false; + /** + * Track whether this resolver needs to perform a measurement + * to resolve its keyframes. + */ + this.needsMeasurement = false; + this.unresolvedKeyframes = [...unresolvedKeyframes]; + this.onComplete = onComplete; + this.name = name; + this.motionValue = motionValue; + this.element = element; + this.isAsync = isAsync; + } + scheduleResolve() { + this.state = "scheduled"; + if (this.isAsync) { + toResolve.add(this); + if (!isScheduled) { + isScheduled = true; + frame.read(readAllKeyframes); + frame.resolveKeyframes(measureAllKeyframes); + } + } + else { + this.readKeyframes(); + this.complete(); + } + } + readKeyframes() { + const { unresolvedKeyframes, name, element, motionValue } = this; + // If initial keyframe is null we need to read it from the DOM + if (unresolvedKeyframes[0] === null) { + const currentValue = motionValue?.get(); + // TODO: This doesn't work if the final keyframe is a wildcard + const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (currentValue !== undefined) { + unresolvedKeyframes[0] = currentValue; + } + else if (element && name) { + const valueAsRead = element.readValue(name, finalKeyframe); + if (valueAsRead !== undefined && valueAsRead !== null) { + unresolvedKeyframes[0] = valueAsRead; + } + } + if (unresolvedKeyframes[0] === undefined) { + unresolvedKeyframes[0] = finalKeyframe; + } + if (motionValue && currentValue === undefined) { + motionValue.set(unresolvedKeyframes[0]); + } + } + fillWildcards(unresolvedKeyframes); + } + setFinalKeyframe() { } + measureInitialState() { } + renderEndStyles() { } + measureEndState() { } + complete(isForcedComplete = false) { + this.state = "complete"; + this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForcedComplete); + toResolve.delete(this); + } + cancel() { + if (this.state === "scheduled") { + toResolve.delete(this); + this.state = "pending"; + } + } + resume() { + if (this.state === "pending") + this.scheduleResolve(); + } + } + + const isCSSVar = (name) => name.startsWith("--"); + + function setStyle(element, name, value) { + isCSSVar(name) + ? element.style.setProperty(name, value) + : (element.style[name] = value); + } + + const supportsScrollTimeline = /* @__PURE__ */ motionUtils.memo(() => window.ScrollTimeline !== undefined); + + /** + * Add the ability for test suites to manually set support flags + * to better test more environments. + */ + const supportsFlags = {}; + + function memoSupports(callback, supportsFlag) { + const memoized = motionUtils.memo(callback); + return () => supportsFlags[supportsFlag] ?? memoized(); + } + + const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => { + try { + document + .createElement("div") + .animate({ opacity: 0 }, { easing: "linear(0, 1)" }); + } + catch (e) { + return false; + } + return true; + }, "linearEasing"); + + const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`; + + const supportedWaapiEasing = { + linear: "linear", + ease: "ease", + easeIn: "ease-in", + easeOut: "ease-out", + easeInOut: "ease-in-out", + circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]), + circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]), + backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]), + backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]), + }; + + function mapEasingToNativeEasing(easing, duration) { + if (!easing) { + return undefined; + } + else if (typeof easing === "function") { + return supportsLinearEasing() + ? generateLinearEasing(easing, duration) + : "ease-out"; + } + else if (motionUtils.isBezierDefinition(easing)) { + return cubicBezierAsString(easing); + } + else if (Array.isArray(easing)) { + return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) || + supportedWaapiEasing.easeOut); + } + else { + return supportedWaapiEasing[easing]; + } + } + + function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) { + const keyframeOptions = { + [valueName]: keyframes, + }; + if (times) + keyframeOptions.offset = times; + const easing = mapEasingToNativeEasing(ease, duration); + /** + * If this is an easing array, apply to keyframes, not animation as a whole + */ + if (Array.isArray(easing)) + keyframeOptions.easing = easing; + if (statsBuffer.value) { + activeAnimations.waapi++; + } + const options = { + delay, + duration, + easing: !Array.isArray(easing) ? easing : "linear", + fill: "both", + iterations: repeat + 1, + direction: repeatType === "reverse" ? "alternate" : "normal", + }; + if (pseudoElement) + options.pseudoElement = pseudoElement; + const animation = element.animate(keyframeOptions, options); + if (statsBuffer.value) { + animation.finished.finally(() => { + activeAnimations.waapi--; + }); + } + return animation; + } + + function isGenerator(type) { + return typeof type === "function" && "applyToOptions" in type; + } + + function applyGeneratorOptions({ type, ...options }) { + if (isGenerator(type) && supportsLinearEasing()) { + return type.applyToOptions(options); + } + else { + options.duration ?? (options.duration = 300); + options.ease ?? (options.ease = "easeOut"); + } + return options; + } + + /** + * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API. + */ + class NativeAnimation extends WithPromise { + constructor(options) { + super(); + this.finishedTime = null; + this.isStopped = false; + if (!options) + return; + const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options; + this.isPseudoElement = Boolean(pseudoElement); + this.allowFlatten = allowFlatten; + this.options = options; + motionUtils.invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`); + const transition = applyGeneratorOptions(options); + this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement); + if (transition.autoplay === false) { + this.animation.pause(); + } + this.animation.onfinish = () => { + this.finishedTime = this.time; + if (!pseudoElement) { + const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed); + if (this.updateMotionValue) { + this.updateMotionValue(keyframe); + } + else { + /** + * If we can, we want to commit the final style as set by the user, + * rather than the computed keyframe value supplied by the animation. + */ + setStyle(element, name, keyframe); + } + this.animation.cancel(); + } + onComplete?.(); + this.notifyFinished(); + }; + } + play() { + if (this.isStopped) + return; + this.animation.play(); + if (this.state === "finished") { + this.updateFinished(); + } + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.finish?.(); + } + cancel() { + try { + this.animation.cancel(); + } + catch (e) { } + } + stop() { + if (this.isStopped) + return; + this.isStopped = true; + const { state } = this; + if (state === "idle" || state === "finished") { + return; + } + if (this.updateMotionValue) { + this.updateMotionValue(); + } + else { + this.commitStyles(); + } + if (!this.isPseudoElement) + this.cancel(); + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * In this method, we commit styles back to the DOM before cancelling + * the animation. + * + * This is designed to be overridden by NativeAnimationExtended, which + * will create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to also correctly calculate velocity for any subsequent animation + * while deferring the commit until the next animation frame. + */ + commitStyles() { + if (!this.isPseudoElement) { + this.animation.commitStyles?.(); + } + } + get duration() { + const duration = this.animation.effect?.getComputedTiming?.().duration || 0; + return motionUtils.millisecondsToSeconds(Number(duration)); + } + get time() { + return motionUtils.millisecondsToSeconds(Number(this.animation.currentTime) || 0); + } + set time(newTime) { + this.finishedTime = null; + this.animation.currentTime = motionUtils.secondsToMilliseconds(newTime); + } + /** + * The playback speed of the animation. + * 1 = normal speed, 2 = double speed, 0.5 = half speed. + */ + get speed() { + return this.animation.playbackRate; + } + set speed(newSpeed) { + // Allow backwards playback after finishing + if (newSpeed < 0) + this.finishedTime = null; + this.animation.playbackRate = newSpeed; + } + get state() { + return this.finishedTime !== null + ? "finished" + : this.animation.playState; + } + get startTime() { + return Number(this.animation.startTime); + } + set startTime(newStartTime) { + this.animation.startTime = newStartTime; + } + /** + * Attaches a timeline to the animation, for instance the `ScrollTimeline`. + */ + attachTimeline({ timeline, observe }) { + if (this.allowFlatten) { + this.animation.effect?.updateTiming({ easing: "linear" }); + } + this.animation.onfinish = null; + if (timeline && supportsScrollTimeline()) { + this.animation.timeline = timeline; + return motionUtils.noop; + } + else { + return observe(this); + } + } + } + + const unsupportedEasingFunctions = { + anticipate: motionUtils.anticipate, + backInOut: motionUtils.backInOut, + circInOut: motionUtils.circInOut, + }; + function isUnsupportedEase(key) { + return key in unsupportedEasingFunctions; + } + function replaceStringEasing(transition) { + if (typeof transition.ease === "string" && + isUnsupportedEase(transition.ease)) { + transition.ease = unsupportedEasingFunctions[transition.ease]; + } + } + + /** + * 10ms is chosen here as it strikes a balance between smooth + * results (more than one keyframe per frame at 60fps) and + * keyframe quantity. + */ + const sampleDelta = 10; //ms + class NativeAnimationExtended extends NativeAnimation { + constructor(options) { + /** + * The base NativeAnimation function only supports a subset + * of Motion easings, and WAAPI also only supports some + * easing functions via string/cubic-bezier definitions. + * + * This function replaces those unsupported easing functions + * with a JS easing function. This will later get compiled + * to a linear() easing function. + */ + replaceStringEasing(options); + /** + * Ensure we replace the transition type with a generator function + * before passing to WAAPI. + * + * TODO: Does this have a better home? It could be shared with + * JSAnimation. + */ + replaceTransitionType(options); + super(options); + if (options.startTime) { + this.startTime = options.startTime; + } + this.options = options; + } + /** + * WAAPI doesn't natively have any interruption capabilities. + * + * Rather than read commited styles back out of the DOM, we can + * create a renderless JS animation and sample it twice to calculate + * its current value, "previous" value, and therefore allow + * Motion to calculate velocity for any subsequent animation. + */ + updateMotionValue(value) { + const { motionValue, onUpdate, onComplete, element, ...options } = this.options; + if (!motionValue) + return; + if (value !== undefined) { + motionValue.set(value); + return; + } + const sampleAnimation = new JSAnimation({ + ...options, + autoplay: false, + }); + const sampleTime = motionUtils.secondsToMilliseconds(this.finishedTime ?? this.time); + motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta); + sampleAnimation.stop(); + } + } + + /** + * Check if a value is animatable. Examples: + * + * ✅: 100, "100px", "#fff" + * ❌: "block", "url(2.jpg)" + * @param value + * + * @internal + */ + const isAnimatable = (value, name) => { + // If the list of keys tat might be non-animatable grows, replace with Set + if (name === "zIndex") + return false; + // If it's a number or a keyframes array, we can animate it. We might at some point + // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this, + // but for now lets leave it like this for performance reasons + if (typeof value === "number" || Array.isArray(value)) + return true; + if (typeof value === "string" && // It's animatable if we have a string + (complex.test(value) || value === "0") && // And it contains numbers and/or colors + !value.startsWith("url(") // Unless it starts with "url(" + ) { + return true; + } + return false; + }; + + function hasKeyframesChanged(keyframes) { + const current = keyframes[0]; + if (keyframes.length === 1) + return true; + for (let i = 0; i < keyframes.length; i++) { + if (keyframes[i] !== current) + return true; + } + } + function canAnimate(keyframes, name, type, velocity) { + /** + * Check if we're able to animate between the start and end keyframes, + * and throw a warning if we're attempting to animate between one that's + * animatable and another that isn't. + */ + const originKeyframe = keyframes[0]; + if (originKeyframe === null) + return false; + /** + * These aren't traditionally animatable but we do support them. + * In future we could look into making this more generic or replacing + * this function with mix() === mixImmediate + */ + if (name === "display" || name === "visibility") + return true; + const targetKeyframe = keyframes[keyframes.length - 1]; + const isOriginAnimatable = isAnimatable(originKeyframe, name); + const isTargetAnimatable = isAnimatable(targetKeyframe, name); + motionUtils.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`); + // Always skip if any of these are true + if (!isOriginAnimatable || !isTargetAnimatable) { + return false; + } + return (hasKeyframesChanged(keyframes) || + ((type === "spring" || isGenerator(type)) && velocity)); + } + + /** + * Checks if an element is an HTML element in a way + * that works across iframes + */ + function isHTMLElement(element) { + return motionUtils.isObject(element) && "offsetHeight" in element; + } + + /** + * A list of values that can be hardware-accelerated. + */ + const acceleratedValues$1 = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Could be re-enabled now we have support for linear() easing + // "background-color" + ]); + const supportsWaapi = /*@__PURE__*/ motionUtils.memo(() => Object.hasOwnProperty.call(Element.prototype, "animate")); + function supportsBrowserAnimation(options) { + const { motionValue, name, repeatDelay, repeatType, damping, type } = options; + if (!isHTMLElement(motionValue?.owner?.current)) { + return false; + } + const { onUpdate, transformTemplate } = motionValue.owner.getProps(); + return (supportsWaapi() && + name && + acceleratedValues$1.has(name) && + (name !== "transform" || !transformTemplate) && + /** + * If we're outputting values to onUpdate then we can't use WAAPI as there's + * no way to read the value from WAAPI every frame. + */ + !onUpdate && + !repeatDelay && + repeatType !== "mirror" && + damping !== 0 && + type !== "inertia"); + } + + /** + * Maximum time allowed between an animation being created and it being + * resolved for us to use the latter as the start time. + * + * This is to ensure that while we prefer to "start" an animation as soon + * as it's triggered, we also want to avoid a visual jump if there's a big delay + * between these two moments. + */ + const MAX_RESOLVE_DELAY = 40; + class AsyncMotionValueAnimation extends WithPromise { + constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) { + super(); + /** + * Bound to support return animation.stop pattern + */ + this.stop = () => { + if (this._animation) { + this._animation.stop(); + this.stopTimeline?.(); + } + this.keyframeResolver?.cancel(); + }; + this.createdAt = time.now(); + const optionsWithDefaults = { + autoplay, + delay, + type, + repeat, + repeatDelay, + repeatType, + name, + motionValue, + element, + ...options, + }; + const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver; + this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element); + this.keyframeResolver?.scheduleResolve(); + } + onKeyframesResolved(keyframes, finalKeyframe, options, sync) { + this.keyframeResolver = undefined; + const { name, type, velocity, delay, isHandoff, onUpdate } = options; + this.resolvedAt = time.now(); + /** + * If we can't animate this value with the resolved keyframes + * then we should complete it immediately. + */ + if (!canAnimate(keyframes, name, type, velocity)) { + if (motionUtils.MotionGlobalConfig.instantAnimations || !delay) { + onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe)); + } + keyframes[0] = keyframes[keyframes.length - 1]; + options.duration = 0; + options.repeat = 0; + } + /** + * Resolve startTime for the animation. + * + * This method uses the createdAt and resolvedAt to calculate the + * animation startTime. *Ideally*, we would use the createdAt time as t=0 + * as the following frame would then be the first frame of the animation in + * progress, which would feel snappier. + * + * However, if there's a delay (main thread work) between the creation of + * the animation and the first commited frame, we prefer to use resolvedAt + * to avoid a sudden jump into the animation. + */ + const startTime = sync + ? !this.resolvedAt + ? this.createdAt + : this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY + ? this.resolvedAt + : this.createdAt + : undefined; + const resolvedOptions = { + startTime, + finalKeyframe, + ...options, + keyframes, + }; + /** + * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via + * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the + * optimised animation. + */ + const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions) + ? new NativeAnimationExtended({ + ...resolvedOptions, + element: resolvedOptions.motionValue.owner.current, + }) + : new JSAnimation(resolvedOptions); + animation.finished.then(() => this.notifyFinished()).catch(motionUtils.noop); + if (this.pendingTimeline) { + this.stopTimeline = animation.attachTimeline(this.pendingTimeline); + this.pendingTimeline = undefined; + } + this._animation = animation; + } + get finished() { + if (!this._animation) { + return this._finished; + } + else { + return this.animation.finished; + } + } + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } + get animation() { + if (!this._animation) { + this.keyframeResolver?.resume(); + flushKeyframeResolvers(); + } + return this._animation; + } + get duration() { + return this.animation.duration; + } + get time() { + return this.animation.time; + } + set time(newTime) { + this.animation.time = newTime; + } + get speed() { + return this.animation.speed; + } + get state() { + return this.animation.state; + } + set speed(newSpeed) { + this.animation.speed = newSpeed; + } + get startTime() { + return this.animation.startTime; + } + attachTimeline(timeline) { + if (this._animation) { + this.stopTimeline = this.animation.attachTimeline(timeline); + } + else { + this.pendingTimeline = timeline; + } + return () => this.stop(); + } + play() { + this.animation.play(); + } + pause() { + this.animation.pause(); + } + complete() { + this.animation.complete(); + } + cancel() { + if (this._animation) { + this.animation.cancel(); + } + this.keyframeResolver?.cancel(); + } + } + + class GroupAnimation { + constructor(animations) { + // Bound to accomadate common `return animation.stop` pattern + this.stop = () => this.runAll("stop"); + this.animations = animations.filter(Boolean); + } + get finished() { + return Promise.all(this.animations.map((animation) => animation.finished)); + } + /** + * TODO: Filter out cancelled or stopped animations before returning + */ + getAll(propName) { + return this.animations[0][propName]; + } + setAll(propName, newValue) { + for (let i = 0; i < this.animations.length; i++) { + this.animations[i][propName] = newValue; + } + } + attachTimeline(timeline) { + const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline)); + return () => { + subscriptions.forEach((cancel, i) => { + cancel && cancel(); + this.animations[i].stop(); + }); + }; + } + get time() { + return this.getAll("time"); + } + set time(time) { + this.setAll("time", time); + } + get speed() { + return this.getAll("speed"); + } + set speed(speed) { + this.setAll("speed", speed); + } + get state() { + return this.getAll("state"); + } + get startTime() { + return this.getAll("startTime"); + } + get duration() { + let max = 0; + for (let i = 0; i < this.animations.length; i++) { + max = Math.max(max, this.animations[i].duration); + } + return max; + } + runAll(methodName) { + this.animations.forEach((controls) => controls[methodName]()); + } + play() { + this.runAll("play"); + } + pause() { + this.runAll("pause"); + } + cancel() { + this.runAll("cancel"); + } + complete() { + this.runAll("complete"); + } + } + + class GroupAnimationWithThen extends GroupAnimation { + then(onResolve, _onReject) { + return this.finished.finally(onResolve).then(() => { }); + } + } + + class NativeAnimationWrapper extends NativeAnimation { + constructor(animation) { + super(); + this.animation = animation; + animation.onfinish = () => { + this.finishedTime = this.time; + this.notifyFinished(); + }; + } + } + + const animationMaps = new WeakMap(); + const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`; + function getAnimationMap(element) { + const map = animationMaps.get(element) || new Map(); + animationMaps.set(element, map); + return map; + } + + /** + * Parse Framer's special CSS variable format into a CSS token and a fallback. + * + * ``` + * `var(--foo, #fff)` => [`--foo`, '#fff'] + * ``` + * + * @param current + */ + const splitCSSVariableRegex = + // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words + /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u; + function parseCSSVariable(current) { + const match = splitCSSVariableRegex.exec(current); + if (!match) + return [,]; + const [, token1, token2, fallback] = match; + return [`--${token1 ?? token2}`, fallback]; + } + const maxDepth = 4; + function getVariableValue(current, element, depth = 1) { + motionUtils.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`); + const [token, fallback] = parseCSSVariable(current); + // No CSS variable detected + if (!token) + return; + // Attempt to read this CSS variable off the element + const resolved = window.getComputedStyle(element).getPropertyValue(token); + if (resolved) { + const trimmed = resolved.trim(); + return motionUtils.isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed; + } + return isCSSVariableToken(fallback) + ? getVariableValue(fallback, element, depth + 1) + : fallback; + } + + function getValueTransition(transition, key) { + return (transition?.[key] ?? + transition?.["default"] ?? + transition); + } + + const positionalKeys = new Set([ + "width", + "height", + "top", + "left", + "right", + "bottom", + ...transformPropOrder, + ]); + + /** + * ValueType for "auto" + */ + const auto = { + test: (v) => v === "auto", + parse: (v) => v, + }; + + /** + * Tests a provided value against a ValueType + */ + const testValueType = (v) => (type) => type.test(v); + + /** + * A list of value types commonly used for dimensions + */ + const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto]; + /** + * Tests a dimensional value against the list of dimension ValueTypes + */ + const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v)); + + function isNone(value) { + if (typeof value === "number") { + return value === 0; + } + else if (value !== null) { + return value === "none" || value === "0" || motionUtils.isZeroValueString(value); + } + else { + return true; + } + } + + /** + * Properties that should default to 1 or 100% + */ + const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]); + function applyDefaultFilter(v) { + const [name, value] = v.slice(0, -1).split("("); + if (name === "drop-shadow") + return v; + const [number] = value.match(floatRegex) || []; + if (!number) + return v; + const unit = value.replace(number, ""); + let defaultValue = maxDefaults.has(name) ? 1 : 0; + if (number !== value) + defaultValue *= 100; + return name + "(" + defaultValue + unit + ")"; + } + const functionRegex = /\b([a-z-]*)\(.*?\)/gu; + const filter = { + ...complex, + getAnimatableNone: (v) => { + const functions = v.match(functionRegex); + return functions ? functions.map(applyDefaultFilter).join(" ") : v; + }, + }; + + const int = { + ...number, + transform: Math.round, + }; + + const transformValueTypes = { + rotate: degrees, + rotateX: degrees, + rotateY: degrees, + rotateZ: degrees, + scale, + scaleX: scale, + scaleY: scale, + scaleZ: scale, + skew: degrees, + skewX: degrees, + skewY: degrees, + distance: px, + translateX: px, + translateY: px, + translateZ: px, + x: px, + y: px, + z: px, + perspective: px, + transformPerspective: px, + opacity: alpha, + originX: progressPercentage, + originY: progressPercentage, + originZ: px, + }; + + const numberValueTypes = { + // Border props + borderWidth: px, + borderTopWidth: px, + borderRightWidth: px, + borderBottomWidth: px, + borderLeftWidth: px, + borderRadius: px, + radius: px, + borderTopLeftRadius: px, + borderTopRightRadius: px, + borderBottomRightRadius: px, + borderBottomLeftRadius: px, + // Positioning props + width: px, + maxWidth: px, + height: px, + maxHeight: px, + top: px, + right: px, + bottom: px, + left: px, + // Spacing props + padding: px, + paddingTop: px, + paddingRight: px, + paddingBottom: px, + paddingLeft: px, + margin: px, + marginTop: px, + marginRight: px, + marginBottom: px, + marginLeft: px, + // Misc + backgroundPositionX: px, + backgroundPositionY: px, + ...transformValueTypes, + zIndex: int, + // SVG + fillOpacity: alpha, + strokeOpacity: alpha, + numOctaves: int, + }; + + /** + * A map of default value types for common values + */ + const defaultValueTypes = { + ...numberValueTypes, + // Color props + color, + backgroundColor: color, + outlineColor: color, + fill: color, + stroke: color, + // Border props + borderColor: color, + borderTopColor: color, + borderRightColor: color, + borderBottomColor: color, + borderLeftColor: color, + filter, + WebkitFilter: filter, + }; + /** + * Gets the default ValueType for the provided value key + */ + const getDefaultValueType = (key) => defaultValueTypes[key]; + + function getAnimatableNone(key, value) { + let defaultValueType = getDefaultValueType(key); + if (defaultValueType !== filter) + defaultValueType = complex; + // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target + return defaultValueType.getAnimatableNone + ? defaultValueType.getAnimatableNone(value) + : undefined; + } + + /** + * If we encounter keyframes like "none" or "0" and we also have keyframes like + * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for + * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into + * zero equivalents, i.e. "#fff0" or "0px 0px". + */ + const invalidTemplates = new Set(["auto", "none", "0"]); + function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) { + let i = 0; + let animatableTemplate = undefined; + while (i < unresolvedKeyframes.length && !animatableTemplate) { + const keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string" && + !invalidTemplates.has(keyframe) && + analyseComplexValue(keyframe).values.length) { + animatableTemplate = unresolvedKeyframes[i]; + } + i++; + } + if (animatableTemplate && name) { + for (const noneIndex of noneKeyframeIndexes) { + unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate); + } + } + } + + class DOMKeyframesResolver extends KeyframeResolver { + constructor(unresolvedKeyframes, onComplete, name, motionValue, element) { + super(unresolvedKeyframes, onComplete, name, motionValue, element, true); + } + readKeyframes() { + const { unresolvedKeyframes, element, name } = this; + if (!element || !element.current) + return; + super.readKeyframes(); + /** + * If any keyframe is a CSS variable, we need to find its value by sampling the element + */ + for (let i = 0; i < unresolvedKeyframes.length; i++) { + let keyframe = unresolvedKeyframes[i]; + if (typeof keyframe === "string") { + keyframe = keyframe.trim(); + if (isCSSVariableToken(keyframe)) { + const resolved = getVariableValue(keyframe, element.current); + if (resolved !== undefined) { + unresolvedKeyframes[i] = resolved; + } + if (i === unresolvedKeyframes.length - 1) { + this.finalKeyframe = keyframe; + } + } + } + } + /** + * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes. + * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which + * have a far bigger performance impact. + */ + this.resolveNoneKeyframes(); + /** + * Check to see if unit type has changed. If so schedule jobs that will + * temporarily set styles to the destination keyframes. + * Skip if we have more than two keyframes or this isn't a positional value. + * TODO: We can throw if there are multiple keyframes and the value type changes. + */ + if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) { + return; + } + const [origin, target] = unresolvedKeyframes; + const originType = findDimensionValueType(origin); + const targetType = findDimensionValueType(target); + /** + * Either we don't recognise these value types or we can animate between them. + */ + if (originType === targetType) + return; + /** + * If both values are numbers or pixels, we can animate between them by + * converting them to numbers. + */ + if (isNumOrPxType(originType) && isNumOrPxType(targetType)) { + for (let i = 0; i < unresolvedKeyframes.length; i++) { + const value = unresolvedKeyframes[i]; + if (typeof value === "string") { + unresolvedKeyframes[i] = parseFloat(value); + } + } + } + else if (positionalValues[name]) { + /** + * Else, the only way to resolve this is by measuring the element. + */ + this.needsMeasurement = true; + } + } + resolveNoneKeyframes() { + const { unresolvedKeyframes, name } = this; + const noneKeyframeIndexes = []; + for (let i = 0; i < unresolvedKeyframes.length; i++) { + if (unresolvedKeyframes[i] === null || + isNone(unresolvedKeyframes[i])) { + noneKeyframeIndexes.push(i); + } + } + if (noneKeyframeIndexes.length) { + makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name); + } + } + measureInitialState() { + const { element, unresolvedKeyframes, name } = this; + if (!element || !element.current) + return; + if (name === "height") { + this.suspendedScrollY = window.pageYOffset; + } + this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + unresolvedKeyframes[0] = this.measuredOrigin; + // Set final key frame to measure after next render + const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1]; + if (measureKeyframe !== undefined) { + element.getValue(name, measureKeyframe).jump(measureKeyframe, false); + } + } + measureEndState() { + const { element, name, unresolvedKeyframes } = this; + if (!element || !element.current) + return; + const value = element.getValue(name); + value && value.jump(this.measuredOrigin, false); + const finalKeyframeIndex = unresolvedKeyframes.length - 1; + const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex]; + unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current)); + if (finalKeyframe !== null && this.finalKeyframe === undefined) { + this.finalKeyframe = finalKeyframe; + } + // If we removed transform values, reapply them before the next render + if (this.removedTransforms?.length) { + this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => { + element + .getValue(unsetTransformName) + .set(unsetTransformValue); + }); + } + this.resolveNoneKeyframes(); + } + } + + const pxValues = new Set([ + // Border props + "borderWidth", + "borderTopWidth", + "borderRightWidth", + "borderBottomWidth", + "borderLeftWidth", + "borderRadius", + "radius", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + // Positioning props + "width", + "maxWidth", + "height", + "maxHeight", + "top", + "right", + "bottom", + "left", + // Spacing props + "padding", + "paddingTop", + "paddingRight", + "paddingBottom", + "paddingLeft", + "margin", + "marginTop", + "marginRight", + "marginBottom", + "marginLeft", + // Misc + "backgroundPositionX", + "backgroundPositionY", + ]); + + function applyPxDefaults(keyframes, name) { + for (let i = 0; i < keyframes.length; i++) { + if (typeof keyframes[i] === "number" && pxValues.has(name)) { + keyframes[i] = keyframes[i] + "px"; + } + } + } + + function isWaapiSupportedEasing(easing) { + return Boolean((typeof easing === "function" && supportsLinearEasing()) || + !easing || + (typeof easing === "string" && + (easing in supportedWaapiEasing || supportsLinearEasing())) || + motionUtils.isBezierDefinition(easing) || + (Array.isArray(easing) && easing.every(isWaapiSupportedEasing))); + } + + const supportsPartialKeyframes = /*@__PURE__*/ motionUtils.memo(() => { + try { + document.createElement("div").animate({ opacity: [1] }); + } + catch (e) { + return false; + } + return true; + }); + + /** + * A list of values that can be hardware-accelerated. + */ + const acceleratedValues = new Set([ + "opacity", + "clipPath", + "filter", + "transform", + // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved + // or until we implement support for linear() easing. + // "background-color" + ]); + + function camelToDash(str) { + return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`); + } + + function resolveElements(elementOrSelector, scope, selectorCache) { + if (elementOrSelector instanceof EventTarget) { + return [elementOrSelector]; + } + else if (typeof elementOrSelector === "string") { + let root = document; + if (scope) { + root = scope.current; + } + const elements = selectorCache?.[elementOrSelector] ?? + root.querySelectorAll(elementOrSelector); + return elements ? Array.from(elements) : []; + } + return Array.from(elementOrSelector); + } + + function createSelectorEffect(subjectEffect) { + return (subject, values) => { + const elements = resolveElements(subject); + const subscriptions = []; + for (const element of elements) { + const remove = subjectEffect(element, values); + subscriptions.push(remove); + } + return () => { + for (const remove of subscriptions) + remove(); + }; + }; + } + + /** + * Provided a value and a ValueType, returns the value as that value type. + */ + const getValueAsType = (value, type) => { + return type && typeof value === "number" + ? type.transform(value) + : value; + }; + + class MotionValueState { + constructor() { + this.latest = {}; + this.values = new Map(); + } + set(name, value, render, computed, useDefaultValueType = true) { + const existingValue = this.values.get(name); + if (existingValue) { + existingValue.onRemove(); + } + const onChange = () => { + const v = value.get(); + if (useDefaultValueType) { + this.latest[name] = getValueAsType(v, numberValueTypes[name]); + } + else { + this.latest[name] = v; + } + render && frame.render(render); + }; + onChange(); + const cancelOnChange = value.on("change", onChange); + computed && value.addDependent(computed); + const remove = () => { + cancelOnChange(); + render && cancelFrame(render); + this.values.delete(name); + computed && value.removeDependent(computed); + }; + this.values.set(name, { value, onRemove: remove }); + return remove; + } + get(name) { + return this.values.get(name)?.value; + } + destroy() { + for (const value of this.values.values()) { + value.onRemove(); + } + } + } + + function createEffect(addValue) { + const stateCache = new WeakMap(); + const subscriptions = []; + return (subject, values) => { + const state = stateCache.get(subject) ?? new MotionValueState(); + stateCache.set(subject, state); + for (const key in values) { + const value = values[key]; + const remove = addValue(subject, state, key, value); + subscriptions.push(remove); + } + return () => { + for (const cancel of subscriptions) + cancel(); + }; + }; + } + + function canSetAsProperty(element, name) { + if (!(name in element)) + return false; + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), name) || + Object.getOwnPropertyDescriptor(element, name); + // Check if it has a setter + return descriptor && typeof descriptor.set === "function"; + } + const addAttrValue = (element, state, key, value) => { + const isProp = canSetAsProperty(element, key); + const name = isProp + ? key + : key.startsWith("data") || key.startsWith("aria") + ? camelToDash(key) + : key; + /** + * Set attribute directly via property if available + */ + const render = isProp + ? () => { + element[name] = state.latest[key]; + } + : () => { + const v = state.latest[key]; + if (v === null || v === undefined) { + element.removeAttribute(name); + } + else { + element.setAttribute(name, String(v)); + } + }; + return state.set(key, value, render); + }; + const attrEffect = /*@__PURE__*/ createSelectorEffect( + /*@__PURE__*/ createEffect(addAttrValue)); + + const propEffect = /*@__PURE__*/ createEffect((subject, state, key, value) => { + return state.set(key, value, () => { + subject[key] = state.latest[key]; + }, undefined, false); + }); + + /** + * Maximum time between the value of two frames, beyond which we + * assume the velocity has since been 0. + */ + const MAX_VELOCITY_DELTA = 30; + const isFloat = (value) => { + return !isNaN(parseFloat(value)); + }; + const collectMotionValues = { + current: undefined, + }; + /** + * `MotionValue` is used to track the state and velocity of motion values. + * + * @public + */ + class MotionValue { + /** + * @param init - The initiating value + * @param config - Optional configuration options + * + * - `transformer`: A function to transform incoming values with. + */ + constructor(init, options = {}) { + /** + * Tracks whether this value can output a velocity. Currently this is only true + * if the value is numerical, but we might be able to widen the scope here and support + * other value types. + * + * @internal + */ + this.canTrackVelocity = null; + /** + * An object containing a SubscriptionManager for each active event. + */ + this.events = {}; + this.updateAndNotify = (v, render = true) => { + const currentTime = time.now(); + /** + * If we're updating the value during another frame or eventloop + * than the previous frame, then the we set the previous frame value + * to current. + */ + if (this.updatedAt !== currentTime) { + this.setPrevFrameValue(); + } + this.prev = this.current; + this.setCurrent(v); + // Update update subscribers + if (this.current !== this.prev) { + this.events.change?.notify(this.current); + if (this.dependents) { + for (const dependent of this.dependents) { + dependent.dirty(); + } + } + } + // Update render subscribers + if (render) { + this.events.renderRequest?.notify(this.current); + } + }; + this.hasAnimated = false; + this.setCurrent(init); + this.owner = options.owner; + } + setCurrent(current) { + this.current = current; + this.updatedAt = time.now(); + if (this.canTrackVelocity === null && current !== undefined) { + this.canTrackVelocity = isFloat(this.current); + } + } + setPrevFrameValue(prevFrameValue = this.current) { + this.prevFrameValue = prevFrameValue; + this.prevUpdatedAt = this.updatedAt; + } + /** + * Adds a function that will be notified when the `MotionValue` is updated. + * + * It returns a function that, when called, will cancel the subscription. + * + * When calling `onChange` inside a React component, it should be wrapped with the + * `useEffect` hook. As it returns an unsubscribe function, this should be returned + * from the `useEffect` function to ensure you don't add duplicate subscribers.. + * + * ```jsx + * export const MyComponent = () => { + * const x = useMotionValue(0) + * const y = useMotionValue(0) + * const opacity = useMotionValue(1) + * + * useEffect(() => { + * function updateOpacity() { + * const maxXY = Math.max(x.get(), y.get()) + * const newOpacity = transform(maxXY, [0, 100], [1, 0]) + * opacity.set(newOpacity) + * } + * + * const unsubscribeX = x.on("change", updateOpacity) + * const unsubscribeY = y.on("change", updateOpacity) + * + * return () => { + * unsubscribeX() + * unsubscribeY() + * } + * }, []) + * + * return + * } + * ``` + * + * @param subscriber - A function that receives the latest value. + * @returns A function that, when called, will cancel this subscription. + * + * @deprecated + */ + onChange(subscription) { + { + motionUtils.warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`); + } + return this.on("change", subscription); + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = new motionUtils.SubscriptionManager(); + } + const unsubscribe = this.events[eventName].add(callback); + if (eventName === "change") { + return () => { + unsubscribe(); + /** + * If we have no more change listeners by the start + * of the next frame, stop active animations. + */ + frame.read(() => { + if (!this.events.change.getSize()) { + this.stop(); + } + }); + }; + } + return unsubscribe; + } + clearListeners() { + for (const eventManagers in this.events) { + this.events[eventManagers].clear(); + } + } + /** + * Attaches a passive effect to the `MotionValue`. + */ + attach(passiveEffect, stopPassiveEffect) { + this.passiveEffect = passiveEffect; + this.stopPassiveEffect = stopPassiveEffect; + } + /** + * Sets the state of the `MotionValue`. + * + * @remarks + * + * ```jsx + * const x = useMotionValue(0) + * x.set(10) + * ``` + * + * @param latest - Latest value to set. + * @param render - Whether to notify render subscribers. Defaults to `true` + * + * @public + */ + set(v, render = true) { + if (!render || !this.passiveEffect) { + this.updateAndNotify(v, render); + } + else { + this.passiveEffect(v, this.updateAndNotify); + } + } + setWithVelocity(prev, current, delta) { + this.set(current); + this.prev = undefined; + this.prevFrameValue = prev; + this.prevUpdatedAt = this.updatedAt - delta; + } + /** + * Set the state of the `MotionValue`, stopping any active animations, + * effects, and resets velocity to `0`. + */ + jump(v, endAnimation = true) { + this.updateAndNotify(v); + this.prev = v; + this.prevUpdatedAt = this.prevFrameValue = undefined; + endAnimation && this.stop(); + if (this.stopPassiveEffect) + this.stopPassiveEffect(); + } + dirty() { + this.events.change?.notify(this.current); + } + addDependent(dependent) { + if (!this.dependents) { + this.dependents = new Set(); + } + this.dependents.add(dependent); + } + removeDependent(dependent) { + if (this.dependents) { + this.dependents.delete(dependent); + } + } + /** + * Returns the latest state of `MotionValue` + * + * @returns - The latest state of `MotionValue` + * + * @public + */ + get() { + if (collectMotionValues.current) { + collectMotionValues.current.push(this); + } + return this.current; + } + /** + * @public + */ + getPrevious() { + return this.prev; + } + /** + * Returns the latest velocity of `MotionValue` + * + * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. + * + * @public + */ + getVelocity() { + const currentTime = time.now(); + if (!this.canTrackVelocity || + this.prevFrameValue === undefined || + currentTime - this.updatedAt > MAX_VELOCITY_DELTA) { + return 0; + } + const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA); + // Casts because of parseFloat's poor typing + return motionUtils.velocityPerSecond(parseFloat(this.current) - + parseFloat(this.prevFrameValue), delta); + } + /** + * Registers a new animation to control this `MotionValue`. Only one + * animation can drive a `MotionValue` at one time. + * + * ```jsx + * value.start() + * ``` + * + * @param animation - A function that starts the provided animation + */ + start(startAnimation) { + this.stop(); + return new Promise((resolve) => { + this.hasAnimated = true; + this.animation = startAnimation(resolve); + if (this.events.animationStart) { + this.events.animationStart.notify(); + } + }).then(() => { + if (this.events.animationComplete) { + this.events.animationComplete.notify(); + } + this.clearAnimation(); + }); + } + /** + * Stop the currently active animation. + * + * @public + */ + stop() { + if (this.animation) { + this.animation.stop(); + if (this.events.animationCancel) { + this.events.animationCancel.notify(); + } + } + this.clearAnimation(); + } + /** + * Returns `true` if this value is currently animating. + * + * @public + */ + isAnimating() { + return !!this.animation; + } + clearAnimation() { + delete this.animation; + } + /** + * Destroy and clean up subscribers to this `MotionValue`. + * + * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically + * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually + * created a `MotionValue` via the `motionValue` function. + * + * @public + */ + destroy() { + this.dependents?.clear(); + this.events.destroy?.notify(); + this.clearListeners(); + this.stop(); + if (this.stopPassiveEffect) { + this.stopPassiveEffect(); + } + } + } + function motionValue(init, options) { + return new MotionValue(init, options); + } + + const translateAlias = { + x: "translateX", + y: "translateY", + z: "translateZ", + transformPerspective: "perspective", + }; + function buildTransform(state) { + let transform = ""; + let transformIsDefault = true; + /** + * Loop over all possible transforms in order, adding the ones that + * are present to the transform string. + */ + for (let i = 0; i < transformPropOrder.length; i++) { + const key = transformPropOrder[i]; + const value = state.latest[key]; + if (value === undefined) + continue; + let valueIsDefault = true; + if (typeof value === "number") { + valueIsDefault = value === (key.startsWith("scale") ? 1 : 0); + } + else { + valueIsDefault = parseFloat(value) === 0; + } + if (!valueIsDefault) { + transformIsDefault = false; + const transformName = translateAlias[key] || key; + const valueToRender = state.latest[key]; + transform += `${transformName}(${valueToRender}) `; + } + } + return transformIsDefault ? "none" : transform.trim(); + } + + const originProps = new Set(["originX", "originY", "originZ"]); + const addStyleValue = (element, state, key, value) => { + let render = undefined; + let computed = undefined; + if (transformProps.has(key)) { + if (!state.get("transform")) { + // If this is an HTML element, we need to set the transform-box to fill-box + // to normalise the transform relative to the element's bounding box + if (!isHTMLElement(element) && !state.get("transformBox")) { + addStyleValue(element, state, "transformBox", new MotionValue("fill-box")); + } + state.set("transform", new MotionValue("none"), () => { + element.style.transform = buildTransform(state); + }); + } + computed = state.get("transform"); + } + else if (originProps.has(key)) { + if (!state.get("transformOrigin")) { + state.set("transformOrigin", new MotionValue(""), () => { + const originX = state.latest.originX ?? "50%"; + const originY = state.latest.originY ?? "50%"; + const originZ = state.latest.originZ ?? 0; + element.style.transformOrigin = `${originX} ${originY} ${originZ}`; + }); + } + computed = state.get("transformOrigin"); + } + else if (isCSSVar(key)) { + render = () => { + element.style.setProperty(key, state.latest[key]); + }; + } + else { + render = () => { + element.style[key] = state.latest[key]; + }; + } + return state.set(key, value, render, computed); + }; + const styleEffect = /*@__PURE__*/ createSelectorEffect( + /*@__PURE__*/ createEffect(addStyleValue)); + + const toPx = px.transform; + function addSVGPathValue(element, state, key, value) { + frame.render(() => element.setAttribute("pathLength", "1")); + if (key === "pathOffset") { + return state.set(key, value, () => element.setAttribute("stroke-dashoffset", toPx(-state.latest[key]))); + } + else { + if (!state.get("stroke-dasharray")) { + state.set("stroke-dasharray", new MotionValue("1 1"), () => { + const { pathLength = 1, pathSpacing } = state.latest; + element.setAttribute("stroke-dasharray", `${toPx(pathLength)} ${toPx(pathSpacing ?? 1 - Number(pathLength))}`); + }); + } + return state.set(key, value, undefined, state.get("stroke-dasharray")); + } + } + const addSVGValue = (element, state, key, value) => { + if (key.startsWith("path")) { + return addSVGPathValue(element, state, key, value); + } + else if (key.startsWith("attr")) { + return addAttrValue(element, state, convertAttrKey(key), value); + } + const handler = key in element.style ? addStyleValue : addAttrValue; + return handler(element, state, key, value); + }; + const svgEffect = /*@__PURE__*/ createSelectorEffect( + /*@__PURE__*/ createEffect(addSVGValue)); + function convertAttrKey(key) { + return key.replace(/^attr([A-Z])/, (_, firstChar) => firstChar.toLowerCase()); + } + + const { schedule: microtask, cancel: cancelMicrotask } = + /* @__PURE__ */ createRenderBatcher(queueMicrotask, false); + + const isDragging = { + x: false, + y: false, + }; + function isDragActive() { + return isDragging.x || isDragging.y; + } + + function setDragLock(axis) { + if (axis === "x" || axis === "y") { + if (isDragging[axis]) { + return null; + } + else { + isDragging[axis] = true; + return () => { + isDragging[axis] = false; + }; + } + } + else { + if (isDragging.x || isDragging.y) { + return null; + } + else { + isDragging.x = isDragging.y = true; + return () => { + isDragging.x = isDragging.y = false; + }; + } + } + } + + function setupGesture(elementOrSelector, options) { + const elements = resolveElements(elementOrSelector); + const gestureAbortController = new AbortController(); + const eventOptions = { + passive: true, + ...options, + signal: gestureAbortController.signal, + }; + const cancel = () => gestureAbortController.abort(); + return [elements, eventOptions, cancel]; + } + + function isValidHover(event) { + return !(event.pointerType === "touch" || isDragActive()); + } + /** + * Create a hover gesture. hover() is different to .addEventListener("pointerenter") + * in that it has an easier syntax, filters out polyfilled touch events, interoperates + * with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends. + * + * @public + */ + function hover(elementOrSelector, onHoverStart, options = {}) { + const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options); + const onPointerEnter = (enterEvent) => { + if (!isValidHover(enterEvent)) + return; + const { target } = enterEvent; + const onHoverEnd = onHoverStart(target, enterEvent); + if (typeof onHoverEnd !== "function" || !target) + return; + const onPointerLeave = (leaveEvent) => { + if (!isValidHover(leaveEvent)) + return; + onHoverEnd(leaveEvent); + target.removeEventListener("pointerleave", onPointerLeave); + }; + target.addEventListener("pointerleave", onPointerLeave, eventOptions); + }; + elements.forEach((element) => { + element.addEventListener("pointerenter", onPointerEnter, eventOptions); + }); + return cancel; + } + + /** + * Recursively traverse up the tree to check whether the provided child node + * is the parent or a descendant of it. + * + * @param parent - Element to find + * @param child - Element to test against parent + */ + const isNodeOrChild = (parent, child) => { + if (!child) { + return false; + } + else if (parent === child) { + return true; + } + else { + return isNodeOrChild(parent, child.parentElement); + } + }; + + const isPrimaryPointer = (event) => { + if (event.pointerType === "mouse") { + return typeof event.button !== "number" || event.button <= 0; + } + else { + /** + * isPrimary is true for all mice buttons, whereas every touch point + * is regarded as its own input. So subsequent concurrent touch points + * will be false. + * + * Specifically match against false here as incomplete versions of + * PointerEvents in very old browser might have it set as undefined. + */ + return event.isPrimary !== false; + } + }; + + const focusableElements = new Set([ + "BUTTON", + "INPUT", + "SELECT", + "TEXTAREA", + "A", + ]); + function isElementKeyboardAccessible(element) { + return (focusableElements.has(element.tagName) || + element.tabIndex !== -1); + } + + const isPressing = new WeakSet(); + + /** + * Filter out events that are not "Enter" keys. + */ + function filterEvents(callback) { + return (event) => { + if (event.key !== "Enter") + return; + callback(event); + }; + } + function firePointerEvent(target, type) { + target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true })); + } + const enableKeyboardPress = (focusEvent, eventOptions) => { + const element = focusEvent.currentTarget; + if (!element) + return; + const handleKeydown = filterEvents(() => { + if (isPressing.has(element)) + return; + firePointerEvent(element, "down"); + const handleKeyup = filterEvents(() => { + firePointerEvent(element, "up"); + }); + const handleBlur = () => firePointerEvent(element, "cancel"); + element.addEventListener("keyup", handleKeyup, eventOptions); + element.addEventListener("blur", handleBlur, eventOptions); + }); + element.addEventListener("keydown", handleKeydown, eventOptions); + /** + * Add an event listener that fires on blur to remove the keydown events. + */ + element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions); + }; + + /** + * Filter out events that are not primary pointer events, or are triggering + * while a Motion gesture is active. + */ + function isValidPressEvent(event) { + return isPrimaryPointer(event) && !isDragActive(); + } + /** + * Create a press gesture. + * + * Press is different to `"pointerdown"`, `"pointerup"` in that it + * automatically filters out secondary pointer events like right + * click and multitouch. + * + * It also adds accessibility support for keyboards, where + * an element with a press gesture will receive focus and + * trigger on Enter `"keydown"` and `"keyup"` events. + * + * This is different to a browser's `"click"` event, which does + * respond to keyboards but only for the `"click"` itself, rather + * than the press start and end/cancel. The element also needs + * to be focusable for this to work, whereas a press gesture will + * make an element focusable by default. + * + * @public + */ + function press(targetOrSelector, onPressStart, options = {}) { + const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options); + const startPress = (startEvent) => { + const target = startEvent.currentTarget; + if (!isValidPressEvent(startEvent)) + return; + isPressing.add(target); + const onPressEnd = onPressStart(target, startEvent); + const onPointerEnd = (endEvent, success) => { + window.removeEventListener("pointerup", onPointerUp); + window.removeEventListener("pointercancel", onPointerCancel); + if (isPressing.has(target)) { + isPressing.delete(target); + } + if (!isValidPressEvent(endEvent)) { + return; + } + if (typeof onPressEnd === "function") { + onPressEnd(endEvent, { success }); + } + }; + const onPointerUp = (upEvent) => { + onPointerEnd(upEvent, target === window || + target === document || + options.useGlobalTarget || + isNodeOrChild(target, upEvent.target)); + }; + const onPointerCancel = (cancelEvent) => { + onPointerEnd(cancelEvent, false); + }; + window.addEventListener("pointerup", onPointerUp, eventOptions); + window.addEventListener("pointercancel", onPointerCancel, eventOptions); + }; + targets.forEach((target) => { + const pointerDownTarget = options.useGlobalTarget ? window : target; + pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions); + if (isHTMLElement(target)) { + target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions)); + if (!isElementKeyboardAccessible(target) && + !target.hasAttribute("tabindex")) { + target.tabIndex = 0; + } + } + }); + return cancelEvents; + } + + function getComputedStyle$1(element, name) { + const computedStyle = window.getComputedStyle(element); + return isCSSVar(name) + ? computedStyle.getPropertyValue(name) + : computedStyle[name]; + } + + /** + * Checks if an element is an SVG element in a way + * that works across iframes + */ + function isSVGElement(element) { + return motionUtils.isObject(element) && "ownerSVGElement" in element; + } + + const resizeHandlers = new WeakMap(); + let observer; + const getSize = (borderBoxAxis, svgAxis, htmlAxis) => (target, borderBoxSize) => { + if (borderBoxSize && borderBoxSize[0]) { + return borderBoxSize[0][(borderBoxAxis + "Size")]; + } + else if (isSVGElement(target) && "getBBox" in target) { + return target.getBBox()[svgAxis]; + } + else { + return target[htmlAxis]; + } + }; + const getWidth = /*@__PURE__*/ getSize("inline", "width", "offsetWidth"); + const getHeight = /*@__PURE__*/ getSize("block", "height", "offsetHeight"); + function notifyTarget({ target, borderBoxSize }) { + resizeHandlers.get(target)?.forEach((handler) => { + handler(target, { + get width() { + return getWidth(target, borderBoxSize); + }, + get height() { + return getHeight(target, borderBoxSize); + }, + }); + }); + } + function notifyAll(entries) { + entries.forEach(notifyTarget); + } + function createResizeObserver() { + if (typeof ResizeObserver === "undefined") + return; + observer = new ResizeObserver(notifyAll); + } + function resizeElement(target, handler) { + if (!observer) + createResizeObserver(); + const elements = resolveElements(target); + elements.forEach((element) => { + let elementHandlers = resizeHandlers.get(element); + if (!elementHandlers) { + elementHandlers = new Set(); + resizeHandlers.set(element, elementHandlers); + } + elementHandlers.add(handler); + observer?.observe(element); + }); + return () => { + elements.forEach((element) => { + const elementHandlers = resizeHandlers.get(element); + elementHandlers?.delete(handler); + if (!elementHandlers?.size) { + observer?.unobserve(element); + } + }); + }; + } + + const windowCallbacks = new Set(); + let windowResizeHandler; + function createWindowResizeHandler() { + windowResizeHandler = () => { + const info = { + get width() { + return window.innerWidth; + }, + get height() { + return window.innerHeight; + }, + }; + windowCallbacks.forEach((callback) => callback(info)); + }; + window.addEventListener("resize", windowResizeHandler); + } + function resizeWindow(callback) { + windowCallbacks.add(callback); + if (!windowResizeHandler) + createWindowResizeHandler(); + return () => { + windowCallbacks.delete(callback); + if (!windowCallbacks.size && + typeof windowResizeHandler === "function") { + window.removeEventListener("resize", windowResizeHandler); + windowResizeHandler = undefined; + } + }; + } + + function resize(a, b) { + return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b); + } + + function observeTimeline(update, timeline) { + let prevProgress; + const onFrame = () => { + const { currentTime } = timeline; + const percentage = currentTime === null ? 0 : currentTime.value; + const progress = percentage / 100; + if (prevProgress !== progress) { + update(progress); + } + prevProgress = progress; + }; + frame.preUpdate(onFrame, true); + return () => cancelFrame(onFrame); + } + + function record() { + const { value } = statsBuffer; + if (value === null) { + cancelFrame(record); + return; + } + value.frameloop.rate.push(frameData.delta); + value.animations.mainThread.push(activeAnimations.mainThread); + value.animations.waapi.push(activeAnimations.waapi); + value.animations.layout.push(activeAnimations.layout); + } + function mean(values) { + return values.reduce((acc, value) => acc + value, 0) / values.length; + } + function summarise(values, calcAverage = mean) { + if (values.length === 0) { + return { + min: 0, + max: 0, + avg: 0, + }; + } + return { + min: Math.min(...values), + max: Math.max(...values), + avg: calcAverage(values), + }; + } + const msToFps = (ms) => Math.round(1000 / ms); + function clearStatsBuffer() { + statsBuffer.value = null; + statsBuffer.addProjectionMetrics = null; + } + function reportStats() { + const { value } = statsBuffer; + if (!value) { + throw new Error("Stats are not being measured"); + } + clearStatsBuffer(); + cancelFrame(record); + const summary = { + frameloop: { + setup: summarise(value.frameloop.setup), + rate: summarise(value.frameloop.rate), + read: summarise(value.frameloop.read), + resolveKeyframes: summarise(value.frameloop.resolveKeyframes), + preUpdate: summarise(value.frameloop.preUpdate), + update: summarise(value.frameloop.update), + preRender: summarise(value.frameloop.preRender), + render: summarise(value.frameloop.render), + postRender: summarise(value.frameloop.postRender), + }, + animations: { + mainThread: summarise(value.animations.mainThread), + waapi: summarise(value.animations.waapi), + layout: summarise(value.animations.layout), + }, + layoutProjection: { + nodes: summarise(value.layoutProjection.nodes), + calculatedTargetDeltas: summarise(value.layoutProjection.calculatedTargetDeltas), + calculatedProjections: summarise(value.layoutProjection.calculatedProjections), + }, + }; + /** + * Convert the rate to FPS + */ + const { rate } = summary.frameloop; + rate.min = msToFps(rate.min); + rate.max = msToFps(rate.max); + rate.avg = msToFps(rate.avg); + [rate.min, rate.max] = [rate.max, rate.min]; + return summary; + } + function recordStats() { + if (statsBuffer.value) { + clearStatsBuffer(); + throw new Error("Stats are already being measured"); + } + const newStatsBuffer = statsBuffer; + newStatsBuffer.value = { + frameloop: { + setup: [], + rate: [], + read: [], + resolveKeyframes: [], + preUpdate: [], + update: [], + preRender: [], + render: [], + postRender: [], + }, + animations: { + mainThread: [], + waapi: [], + layout: [], + }, + layoutProjection: { + nodes: [], + calculatedTargetDeltas: [], + calculatedProjections: [], + }, + }; + newStatsBuffer.addProjectionMetrics = (metrics) => { + const { layoutProjection } = newStatsBuffer.value; + layoutProjection.nodes.push(metrics.nodes); + layoutProjection.calculatedTargetDeltas.push(metrics.calculatedTargetDeltas); + layoutProjection.calculatedProjections.push(metrics.calculatedProjections); + }; + frame.postRender(record, true); + return reportStats; + } + + /** + * Checks if an element is specifically an SVGSVGElement (the root SVG element) + * in a way that works across iframes + */ + function isSVGSVGElement(element) { + return isSVGElement(element) && element.tagName === "svg"; + } + + function getOriginIndex(from, total) { + if (from === "first") { + return 0; + } + else { + const lastIndex = total - 1; + return from === "last" ? lastIndex : lastIndex / 2; + } + } + function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) { + return (i, total) => { + const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total); + const distance = Math.abs(fromIndex - i); + let delay = duration * distance; + if (ease) { + const maxDelay = total * duration; + const easingFunction = motionUtils.easingDefinitionToFunction(ease); + delay = easingFunction(delay / maxDelay) * maxDelay; + } + return startDelay + delay; + }; + } + + function transform(...args) { + const useImmediate = !Array.isArray(args[0]); + const argOffset = useImmediate ? 0 : -1; + const inputValue = args[0 + argOffset]; + const inputRange = args[1 + argOffset]; + const outputRange = args[2 + argOffset]; + const options = args[3 + argOffset]; + const interpolator = interpolate(inputRange, outputRange, options); + return useImmediate ? interpolator(inputValue) : interpolator; + } + + function subscribeValue(inputValues, outputValue, getLatest) { + const update = () => outputValue.set(getLatest()); + const scheduleUpdate = () => frame.preRender(update, false, true); + const subscriptions = inputValues.map((v) => v.on("change", scheduleUpdate)); + outputValue.on("destroy", () => { + subscriptions.forEach((unsubscribe) => unsubscribe()); + cancelFrame(update); + }); + } + + /** + * Create a `MotionValue` that transforms the output of other `MotionValue`s by + * passing their latest values through a transform function. + * + * Whenever a `MotionValue` referred to in the provided function is updated, + * it will be re-evaluated. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ + function transformValue(transform) { + const collectedValues = []; + /** + * Open session of collectMotionValues. Any MotionValue that calls get() + * inside transform will be saved into this array. + */ + collectMotionValues.current = collectedValues; + const initialValue = transform(); + collectMotionValues.current = undefined; + const value = motionValue(initialValue); + subscribeValue(collectedValues, value, transform); + return value; + } + + /** + * Create a `MotionValue` that maps the output of another `MotionValue` by + * mapping it from one range of values into another. + * + * @remarks + * + * Given an input range of `[-200, -100, 100, 200]` and an output range of + * `[0, 1, 1, 0]`, the returned `MotionValue` will: + * + * - When provided a value between `-200` and `-100`, will return a value between `0` and `1`. + * - When provided a value between `-100` and `100`, will return `1`. + * - When provided a value between `100` and `200`, will return a value between `1` and `0` + * + * The input range must be a linear series of numbers. The output range + * can be any value type supported by Motion: numbers, colors, shadows, etc. + * + * Every value in the output range must be of the same type and in the same format. + * + * ```jsx + * const x = motionValue(0) + * const xRange = [-200, -100, 100, 200] + * const opacityRange = [0, 1, 1, 0] + * const opacity = mapValue(x, xRange, opacityRange) + * ``` + * + * @param inputValue - `MotionValue` + * @param inputRange - A linear series of numbers (either all increasing or decreasing) + * @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`. + * @param options - + * + * - clamp: boolean. Clamp values to within the given range. Defaults to `true` + * - ease: EasingFunction[]. Easing functions to use on the interpolations between each value in the input and output ranges. If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition between each. + * + * @returns `MotionValue` + * + * @public + */ + function mapValue(inputValue, inputRange, outputRange, options) { + const map = transform(inputRange, outputRange, options); + return transformValue(() => map(inputValue.get())); + } + + const isMotionValue = (value) => Boolean(value && value.getVelocity); + + /** + * Create a `MotionValue` that animates to its latest value using a spring. + * Can either be a value or track another `MotionValue`. + * + * ```jsx + * const x = motionValue(0) + * const y = transformValue(() => x.get() * 2) // double x + * ``` + * + * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements. + * @returns `MotionValue` + * + * @public + */ + function springValue(source, options) { + const initialValue = isMotionValue(source) ? source.get() : source; + const value = motionValue(initialValue); + attachSpring(value, source, options); + return value; + } + function attachSpring(value, source, options) { + const initialValue = value.get(); + let activeAnimation = null; + let latestValue = initialValue; + let latestSetter; + const unit = typeof initialValue === "string" + ? initialValue.replace(/[\d.-]/g, "") + : undefined; + const stopAnimation = () => { + if (activeAnimation) { + activeAnimation.stop(); + activeAnimation = null; + } + }; + const startAnimation = () => { + stopAnimation(); + activeAnimation = new JSAnimation({ + keyframes: [asNumber(value.get()), asNumber(latestValue)], + velocity: value.getVelocity(), + type: "spring", + restDelta: 0.001, + restSpeed: 0.01, + ...options, + onUpdate: latestSetter, + }); + }; + value.attach((v, set) => { + latestValue = v; + latestSetter = (latest) => set(parseValue(latest, unit)); + frame.postRender(startAnimation); + return value.get(); + }, stopAnimation); + let unsubscribe = undefined; + if (isMotionValue(source)) { + unsubscribe = source.on("change", (v) => value.set(parseValue(v, unit))); + value.on("destroy", unsubscribe); + } + return unsubscribe; + } + function parseValue(v, unit) { + return unit ? v + unit : v; + } + function asNumber(v) { + return typeof v === "number" ? v : parseFloat(v); + } + + /** + * A list of all ValueTypes + */ + const valueTypes = [...dimensionValueTypes, color, complex]; + /** + * Tests a value against the list of ValueTypes + */ + const findValueType = (v) => valueTypes.find(testValueType(v)); + + function chooseLayerType(valueName) { + if (valueName === "layout") + return "group"; + if (valueName === "enter" || valueName === "new") + return "new"; + if (valueName === "exit" || valueName === "old") + return "old"; + return "group"; + } + + let pendingRules = {}; + let style = null; + const css = { + set: (selector, values) => { + pendingRules[selector] = values; + }, + commit: () => { + if (!style) { + style = document.createElement("style"); + style.id = "motion-view"; + } + let cssText = ""; + for (const selector in pendingRules) { + const rule = pendingRules[selector]; + cssText += `${selector} {\n`; + for (const [property, value] of Object.entries(rule)) { + cssText += ` ${property}: ${value};\n`; + } + cssText += "}\n"; + } + style.textContent = cssText; + document.head.appendChild(style); + pendingRules = {}; + }, + remove: () => { + if (style && style.parentElement) { + style.parentElement.removeChild(style); + } + }, + }; + + function getLayerName(pseudoElement) { + const match = pseudoElement.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/); + if (!match) + return null; + return { layer: match[2], type: match[1] }; + } + + function filterViewAnimations(animation) { + const { effect } = animation; + if (!effect) + return false; + return (effect.target === document.documentElement && + effect.pseudoElement?.startsWith("::view-transition")); + } + function getViewAnimations() { + return document.getAnimations().filter(filterViewAnimations); + } + + function hasTarget(target, targets) { + return targets.has(target) && Object.keys(targets.get(target)).length > 0; + } + + const definitionNames = ["layout", "enter", "exit", "new", "old"]; + function startViewAnimation(builder) { + const { update, targets, options: defaultOptions } = builder; + if (!document.startViewTransition) { + return new Promise(async (resolve) => { + await update(); + resolve(new GroupAnimation([])); + }); + } + // TODO: Go over existing targets and ensure they all have ids + /** + * If we don't have any animations defined for the root target, + * remove it from being captured. + */ + if (!hasTarget("root", targets)) { + css.set(":root", { + "view-transition-name": "none", + }); + } + /** + * Set the timing curve to linear for all view transition layers. + * This gets baked into the keyframes, which can't be changed + * without breaking the generated animation. + * + * This allows us to set easing via updateTiming - which can be changed. + */ + css.set("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)", { "animation-timing-function": "linear !important" }); + css.commit(); // Write + const transition = document.startViewTransition(async () => { + await update(); + // TODO: Go over new targets and ensure they all have ids + }); + transition.finished.finally(() => { + css.remove(); // Write + }); + return new Promise((resolve) => { + transition.ready.then(() => { + const generatedViewAnimations = getViewAnimations(); + const animations = []; + /** + * Create animations for each of our explicitly-defined subjects. + */ + targets.forEach((definition, target) => { + // TODO: If target is not "root", resolve elements + // and iterate over each + for (const key of definitionNames) { + if (!definition[key]) + continue; + const { keyframes, options } = definition[key]; + for (let [valueName, valueKeyframes] of Object.entries(keyframes)) { + if (!valueKeyframes) + continue; + const valueOptions = { + ...getValueTransition(defaultOptions, valueName), + ...getValueTransition(options, valueName), + }; + const type = chooseLayerType(key); + /** + * If this is an opacity animation, and keyframes are not an array, + * we need to convert them into an array and set an initial value. + */ + if (valueName === "opacity" && + !Array.isArray(valueKeyframes)) { + const initialValue = type === "new" ? 0 : 1; + valueKeyframes = [initialValue, valueKeyframes]; + } + /** + * Resolve stagger function if provided. + */ + if (typeof valueOptions.delay === "function") { + valueOptions.delay = valueOptions.delay(0, 1); + } + valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration)); + valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay)); + const animation = new NativeAnimation({ + ...valueOptions, + element: document.documentElement, + name: valueName, + pseudoElement: `::view-transition-${type}(${target})`, + keyframes: valueKeyframes, + }); + animations.push(animation); + } + } + }); + /** + * Handle browser generated animations + */ + for (const animation of generatedViewAnimations) { + if (animation.playState === "finished") + continue; + const { effect } = animation; + if (!effect || !(effect instanceof KeyframeEffect)) + continue; + const { pseudoElement } = effect; + if (!pseudoElement) + continue; + const name = getLayerName(pseudoElement); + if (!name) + continue; + const targetDefinition = targets.get(name.layer); + if (!targetDefinition) { + /** + * If transition name is group then update the timing of the animation + * whereas if it's old or new then we could possibly replace it using + * the above method. + */ + const transitionName = name.type === "group" ? "layout" : ""; + let animationTransition = { + ...getValueTransition(defaultOptions, transitionName), + }; + animationTransition.duration && (animationTransition.duration = motionUtils.secondsToMilliseconds(animationTransition.duration)); + animationTransition = + applyGeneratorOptions(animationTransition); + const easing = mapEasingToNativeEasing(animationTransition.ease, animationTransition.duration); + effect.updateTiming({ + delay: motionUtils.secondsToMilliseconds(animationTransition.delay ?? 0), + duration: animationTransition.duration, + easing, + }); + animations.push(new NativeAnimationWrapper(animation)); + } + else if (hasOpacity(targetDefinition, "enter") && + hasOpacity(targetDefinition, "exit") && + effect + .getKeyframes() + .some((keyframe) => keyframe.mixBlendMode)) { + animations.push(new NativeAnimationWrapper(animation)); + } + else { + animation.cancel(); + } + } + resolve(new GroupAnimation(animations)); + }); + }); + } + function hasOpacity(target, key) { + return target?.[key]?.keyframes.opacity; + } + + let builders = []; + let current = null; + function next() { + current = null; + const [nextBuilder] = builders; + if (nextBuilder) + start(nextBuilder); + } + function start(builder) { + motionUtils.removeItem(builders, builder); + current = builder; + startViewAnimation(builder).then((animation) => { + builder.notifyReady(animation); + animation.finished.finally(next); + }); + } + function processQueue() { + /** + * Iterate backwards over the builders array. We can ignore the + * "wait" animations. If we have an interrupting animation in the + * queue then we need to batch all preceeding animations into it. + * Currently this only batches the update functions but will also + * need to batch the targets. + */ + for (let i = builders.length - 1; i >= 0; i--) { + const builder = builders[i]; + const { interrupt } = builder.options; + if (interrupt === "immediate") { + const batchedUpdates = builders.slice(0, i + 1).map((b) => b.update); + const remaining = builders.slice(i + 1); + builder.update = () => { + batchedUpdates.forEach((update) => update()); + }; + // Put the current builder at the front, followed by any "wait" builders + builders = [builder, ...remaining]; + break; + } + } + if (!current || builders[0]?.options.interrupt === "immediate") { + next(); + } + } + function addToQueue(builder) { + builders.push(builder); + microtask.render(processQueue); + } + + class ViewTransitionBuilder { + constructor(update, options = {}) { + this.currentSubject = "root"; + this.targets = new Map(); + this.notifyReady = motionUtils.noop; + this.readyPromise = new Promise((resolve) => { + this.notifyReady = resolve; + }); + this.update = update; + this.options = { + interrupt: "wait", + ...options, + }; + addToQueue(this); + } + get(subject) { + this.currentSubject = subject; + return this; + } + layout(keyframes, options) { + this.updateTarget("layout", keyframes, options); + return this; + } + new(keyframes, options) { + this.updateTarget("new", keyframes, options); + return this; + } + old(keyframes, options) { + this.updateTarget("old", keyframes, options); + return this; + } + enter(keyframes, options) { + this.updateTarget("enter", keyframes, options); + return this; + } + exit(keyframes, options) { + this.updateTarget("exit", keyframes, options); + return this; + } + crossfade(options) { + this.updateTarget("enter", { opacity: 1 }, options); + this.updateTarget("exit", { opacity: 0 }, options); + return this; + } + updateTarget(target, keyframes, options = {}) { + const { currentSubject, targets } = this; + if (!targets.has(currentSubject)) { + targets.set(currentSubject, {}); + } + const targetData = targets.get(currentSubject); + targetData[target] = { keyframes, options }; + } + then(resolve, reject) { + return this.readyPromise.then(resolve, reject); + } + } + function animateView(update, defaultOptions = {}) { + return new ViewTransitionBuilder(update, defaultOptions); + } + + /** + * @deprecated + * + * Import as `frame` instead. + */ + const sync = frame; + /** + * @deprecated + * + * Use cancelFrame(callback) instead. + */ + const cancelSync = stepsOrder.reduce((acc, key) => { + acc[key] = (process) => cancelFrame(process); + return acc; + }, {}); + + exports.AsyncMotionValueAnimation = AsyncMotionValueAnimation; + exports.DOMKeyframesResolver = DOMKeyframesResolver; + exports.GroupAnimation = GroupAnimation; + exports.GroupAnimationWithThen = GroupAnimationWithThen; + exports.JSAnimation = JSAnimation; + exports.KeyframeResolver = KeyframeResolver; + exports.MotionValue = MotionValue; + exports.NativeAnimation = NativeAnimation; + exports.NativeAnimationExtended = NativeAnimationExtended; + exports.NativeAnimationWrapper = NativeAnimationWrapper; + exports.ViewTransitionBuilder = ViewTransitionBuilder; + exports.acceleratedValues = acceleratedValues; + exports.activeAnimations = activeAnimations; + exports.addAttrValue = addAttrValue; + exports.addStyleValue = addStyleValue; + exports.alpha = alpha; + exports.analyseComplexValue = analyseComplexValue; + exports.animateValue = animateValue; + exports.animateView = animateView; + exports.animationMapKey = animationMapKey; + exports.applyPxDefaults = applyPxDefaults; + exports.attachSpring = attachSpring; + exports.attrEffect = attrEffect; + exports.calcGeneratorDuration = calcGeneratorDuration; + exports.cancelFrame = cancelFrame; + exports.cancelMicrotask = cancelMicrotask; + exports.cancelSync = cancelSync; + exports.collectMotionValues = collectMotionValues; + exports.color = color; + exports.complex = complex; + exports.convertOffsetToTimes = convertOffsetToTimes; + exports.createGeneratorEasing = createGeneratorEasing; + exports.createRenderBatcher = createRenderBatcher; + exports.cubicBezierAsString = cubicBezierAsString; + exports.defaultEasing = defaultEasing; + exports.defaultOffset = defaultOffset; + exports.defaultTransformValue = defaultTransformValue; + exports.defaultValueTypes = defaultValueTypes; + exports.degrees = degrees; + exports.dimensionValueTypes = dimensionValueTypes; + exports.fillOffset = fillOffset; + exports.fillWildcards = fillWildcards; + exports.findDimensionValueType = findDimensionValueType; + exports.findValueType = findValueType; + exports.flushKeyframeResolvers = flushKeyframeResolvers; + exports.frame = frame; + exports.frameData = frameData; + exports.frameSteps = frameSteps; + exports.generateLinearEasing = generateLinearEasing; + exports.getAnimatableNone = getAnimatableNone; + exports.getAnimationMap = getAnimationMap; + exports.getComputedStyle = getComputedStyle$1; + exports.getDefaultValueType = getDefaultValueType; + exports.getMixer = getMixer; + exports.getOriginIndex = getOriginIndex; + exports.getValueAsType = getValueAsType; + exports.getValueTransition = getValueTransition; + exports.getVariableValue = getVariableValue; + exports.hex = hex; + exports.hover = hover; + exports.hsla = hsla; + exports.hslaToRgba = hslaToRgba; + exports.inertia = inertia; + exports.interpolate = interpolate; + exports.invisibleValues = invisibleValues; + exports.isCSSVariableName = isCSSVariableName; + exports.isCSSVariableToken = isCSSVariableToken; + exports.isDragActive = isDragActive; + exports.isDragging = isDragging; + exports.isGenerator = isGenerator; + exports.isHTMLElement = isHTMLElement; + exports.isMotionValue = isMotionValue; + exports.isNodeOrChild = isNodeOrChild; + exports.isPrimaryPointer = isPrimaryPointer; + exports.isSVGElement = isSVGElement; + exports.isSVGSVGElement = isSVGSVGElement; + exports.isWaapiSupportedEasing = isWaapiSupportedEasing; + exports.keyframes = keyframes; + exports.mapEasingToNativeEasing = mapEasingToNativeEasing; + exports.mapValue = mapValue; + exports.maxGeneratorDuration = maxGeneratorDuration; + exports.microtask = microtask; + exports.mix = mix; + exports.mixArray = mixArray; + exports.mixColor = mixColor; + exports.mixComplex = mixComplex; + exports.mixImmediate = mixImmediate; + exports.mixLinearColor = mixLinearColor; + exports.mixNumber = mixNumber$1; + exports.mixObject = mixObject; + exports.mixVisibility = mixVisibility; + exports.motionValue = motionValue; + exports.number = number; + exports.numberValueTypes = numberValueTypes; + exports.observeTimeline = observeTimeline; + exports.parseCSSVariable = parseCSSVariable; + exports.parseValueFromTransform = parseValueFromTransform; + exports.percent = percent; + exports.positionalKeys = positionalKeys; + exports.press = press; + exports.progressPercentage = progressPercentage; + exports.propEffect = propEffect; + exports.px = px; + exports.readTransformValue = readTransformValue; + exports.recordStats = recordStats; + exports.resize = resize; + exports.resolveElements = resolveElements; + exports.rgbUnit = rgbUnit; + exports.rgba = rgba; + exports.scale = scale; + exports.setDragLock = setDragLock; + exports.setStyle = setStyle; + exports.spring = spring; + exports.springValue = springValue; + exports.stagger = stagger; + exports.startWaapiAnimation = startWaapiAnimation; + exports.statsBuffer = statsBuffer; + exports.styleEffect = styleEffect; + exports.supportedWaapiEasing = supportedWaapiEasing; + exports.supportsBrowserAnimation = supportsBrowserAnimation; + exports.supportsFlags = supportsFlags; + exports.supportsLinearEasing = supportsLinearEasing; + exports.supportsPartialKeyframes = supportsPartialKeyframes; + exports.supportsScrollTimeline = supportsScrollTimeline; + exports.svgEffect = svgEffect; + exports.sync = sync; + exports.testValueType = testValueType; + exports.time = time; + exports.transform = transform; + exports.transformPropOrder = transformPropOrder; + exports.transformProps = transformProps; + exports.transformValue = transformValue; + exports.transformValueTypes = transformValueTypes; + exports.vh = vh; + exports.vw = vw; + +})); diff --git a/node_modules/motion-dom/dist/motion-dom.js b/node_modules/motion-dom/dist/motion-dom.js new file mode 100644 index 00000000..878ff933 --- /dev/null +++ b/node_modules/motion-dom/dist/motion-dom.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("motion-utils")):"function"==typeof define&&define.amd?define(["exports","motion-utils"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).MotionDom={},t.MotionUtils)}(this,function(t,e){"use strict";const n=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],s={value:null,addProjectionMetrics:null};function i(t,i){let r=!1,a=!0;const o={delta:0,timestamp:0,isProcessing:!1},l=()=>r=!0,u=n.reduce((t,e)=>(t[e]=function(t,e){let n=new Set,i=new Set,r=!1,a=!1;const o=new WeakSet;let l={delta:0,timestamp:0,isProcessing:!1},u=0;function c(e){o.has(e)&&(h.schedule(e),t()),u++,e(l)}const h={schedule:(t,e=!1,s=!1)=>{const a=s&&r?n:i;return e&&o.add(t),a.has(t)||a.add(t),t},cancel:t=>{i.delete(t),o.delete(t)},process:t=>{l=t,r?a=!0:(r=!0,[n,i]=[i,n],n.forEach(c),e&&s.value&&s.value.frameloop[e].push(u),u=0,n.clear(),r=!1,a&&(a=!1,h.process(t)))}};return h}(l,i?e:void 0),t),{}),{setup:c,read:h,resolveKeyframes:d,preUpdate:m,update:p,preRender:f,render:g,postRender:y}=u,v=()=>{const n=e.MotionGlobalConfig.useManualTiming?o.timestamp:performance.now();r=!1,e.MotionGlobalConfig.useManualTiming||(o.delta=a?1e3/60:Math.max(Math.min(n-o.timestamp,40),1)),o.timestamp=n,o.isProcessing=!0,c.process(o),h.process(o),d.process(o),m.process(o),p.process(o),f.process(o),g.process(o),y.process(o),o.isProcessing=!1,r&&i&&(a=!1,t(v))};return{schedule:n.reduce((e,n)=>{const s=u[n];return e[n]=(e,n=!1,i=!1)=>(r||(r=!0,a=!0,o.isProcessing||t(v)),s.schedule(e,n,i)),e},{}),cancel:t=>{for(let e=0;e(void 0===u&&h.set(o.isProcessing||e.MotionGlobalConfig.useManualTiming?o.timestamp:performance.now()),u),set:t=>{u=t,queueMicrotask(c)}},d={layout:0,mainThread:0,waapi:0},m=t=>e=>"string"==typeof e&&e.startsWith(t),p=m("--"),f=m("var(--"),g=t=>!!f(t)&&y.test(t.split("/*")[0].trim()),y=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu,v={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},b={...v,transform:t=>e.clamp(0,1,t)},T={...v,default:1},w=t=>Math.round(1e5*t)/1e5,M=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const x=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,S=(t,e)=>n=>Boolean("string"==typeof n&&x.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),A=(t,e,n)=>s=>{if("string"!=typeof s)return s;const[i,r,a,o]=s.match(M);return{[t]:parseFloat(i),[e]:parseFloat(r),[n]:parseFloat(a),alpha:void 0!==o?parseFloat(o):1}},k={...v,transform:t=>Math.round((t=>e.clamp(0,255,t))(t))},E={test:S("rgb","red"),parse:A("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:s=1})=>"rgba("+k.transform(t)+", "+k.transform(e)+", "+k.transform(n)+", "+w(b.transform(s))+")"};const V={test:S("#"),parse:function(t){let e="",n="",s="",i="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),s=t.substring(5,7),i=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),s=t.substring(3,4),i=t.substring(4,5),e+=e,n+=n,s+=s,i+=i),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(s,16),alpha:i?parseInt(i,16)/255:1}},transform:E.transform},P=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),R=P("deg"),D=P("%"),F=P("px"),O=P("vh"),C=P("vw"),K=(()=>({...D,parse:t=>D.parse(t)/100,transform:t=>D.transform(100*t)}))(),W={test:S("hsl","hue"),parse:A("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:s=1})=>"hsla("+Math.round(t)+", "+D.transform(w(e))+", "+D.transform(w(n))+", "+w(b.transform(s))+")"},L={test:t=>E.test(t)||V.test(t)||W.test(t),parse:t=>E.test(t)?E.parse(t):W.test(t)?W.parse(t):V.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?E.transform(t):W.transform(t),getAnimatableNone:t=>{const e=L.parse(t);return e.alpha=0,L.transform(e)}},B=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const $="number",N="color",j=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function I(t){const e=t.toString(),n=[],s={color:[],number:[],var:[]},i=[];let r=0;const a=e.replace(j,t=>(L.test(t)?(s.color.push(r),i.push(N),n.push(L.parse(t))):t.startsWith("var(")?(s.var.push(r),i.push("var"),n.push(t)):(s.number.push(r),i.push($),n.push(parseFloat(t))),++r,"${}")).split("${}");return{values:n,split:a,indexes:s,types:i}}function Y(t){return I(t).values}function z(t){const{split:e,types:n}=I(t),s=e.length;return t=>{let i="";for(let r=0;r"number"==typeof t?0:L.test(t)?L.getAnimatableNone(t):t;const U={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(M)?.length||0)+(t.match(B)?.length||0)>0},parse:Y,createTransformer:z,getAnimatableNone:function(t){const e=Y(t);return z(t)(e.map(X))}};function G(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function q({hue:t,saturation:e,lightness:n,alpha:s}){t/=360,n/=100;let i=0,r=0,a=0;if(e/=100){const s=n<.5?n*(1+e):n+e-n*e,o=2*n-s;i=G(o,s,t+1/3),r=G(o,s,t),a=G(o,s,t-1/3)}else i=r=a=n;return{red:Math.round(255*i),green:Math.round(255*r),blue:Math.round(255*a),alpha:s}}function Z(t,e){return n=>n>0?e:t}const _=(t,e,n)=>t+(e-t)*n,H=(t,e,n)=>{const s=t*t,i=n*(e*e-s)+s;return i<0?0:Math.sqrt(i)},J=[V,E,W];function Q(t){const n=(s=t,J.find(t=>t.test(s)));var s;if(e.warning(Boolean(n),`'${t}' is not an animatable color. Use the equivalent color code instead.`),!Boolean(n))return!1;let i=n.parse(t);return n===W&&(i=q(i)),i}const tt=(t,e)=>{const n=Q(t),s=Q(e);if(!n||!s)return Z(t,e);const i={...n};return t=>(i.red=H(n.red,s.red,t),i.green=H(n.green,s.green,t),i.blue=H(n.blue,s.blue,t),i.alpha=_(n.alpha,s.alpha,t),E.transform(i))},et=new Set(["none","hidden"]);function nt(t,e){return et.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}function st(t,e){return n=>_(t,e,n)}function it(t){return"number"==typeof t?st:"string"==typeof t?g(t)?Z:L.test(t)?tt:ot:Array.isArray(t)?rt:"object"==typeof t?L.test(t)?tt:at:Z}function rt(t,e){const n=[...t],s=n.length,i=t.map((t,n)=>it(t)(t,e[n]));return t=>{for(let e=0;e{for(const e in s)n[e]=s[e](t);return n}}const ot=(t,n)=>{const s=U.createTransformer(n),i=I(t),r=I(n);return i.indexes.var.length===r.indexes.var.length&&i.indexes.color.length===r.indexes.color.length&&i.indexes.number.length>=r.indexes.number.length?et.has(t)&&!r.values.length||et.has(n)&&!i.values.length?nt(t,n):e.pipe(rt(function(t,e){const n=[],s={color:0,var:0,number:0};for(let i=0;i{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>r.update(e,t),stop:()=>a(e),now:()=>o.isProcessing?o.timestamp:h.now()}},ct=(t,e,n=10)=>{let s="";const i=Math.max(Math.round(e/n),2);for(let e=0;e=ht?1/0:e}function mt(t,n=100,s){const i=s({...t,keyframes:[0,n]}),r=Math.min(dt(i),ht);return{type:"keyframes",ease:t=>i.next(r*t).value/n,duration:e.millisecondsToSeconds(r)}}function pt(t,n,s){const i=Math.max(n-5,0);return e.velocityPerSecond(s-t(i),n-i)}const ft=100,gt=10,yt=1,vt=0,bt=800,Tt=.3,wt=.3,Mt={granular:.01,default:2},xt={granular:.005,default:.5},St=.01,At=10,kt=.05,Et=1,Vt=.001;function Pt({duration:t=bt,bounce:n=Tt,velocity:s=vt,mass:i=yt}){let r,a;e.warning(t<=e.secondsToMilliseconds(At),"Spring duration must be 10 seconds or less");let o=1-n;o=e.clamp(kt,Et,o),t=e.clamp(St,At,e.millisecondsToSeconds(t)),o<1?(r=e=>{const n=e*o,i=n*t,r=n-s,a=Dt(e,o),l=Math.exp(-i);return Vt-r/a*l},a=e=>{const n=e*o*t,i=n*s+s,a=Math.pow(o,2)*Math.pow(e,2)*t,l=Math.exp(-n),u=Dt(Math.pow(e,2),o);return(-r(e)+Vt>0?-1:1)*((i-a)*l)/u}):(r=e=>Math.exp(-e*t)*((e-s)*t+1)-.001,a=e=>Math.exp(-e*t)*(t*t*(s-e)));const l=function(t,e,n){let s=n;for(let n=1;nvoid 0!==t[e])}function Kt(t=wt,n=Tt){const s="object"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:n}:t;let{restSpeed:i,restDelta:r}=s;const a=s.keyframes[0],o=s.keyframes[s.keyframes.length-1],l={done:!1,value:a},{stiffness:u,damping:c,mass:h,duration:d,velocity:m,isResolvedFromDuration:p}=function(t){let n={velocity:vt,stiffness:ft,damping:gt,mass:yt,isResolvedFromDuration:!1,...t};if(!Ct(t,Ot)&&Ct(t,Ft))if(t.visualDuration){const s=t.visualDuration,i=2*Math.PI/(1.2*s),r=i*i,a=2*e.clamp(.05,1,1-(t.bounce||0))*Math.sqrt(r);n={...n,mass:yt,stiffness:r,damping:a}}else{const e=Pt(t);n={...n,...e,mass:yt},n.isResolvedFromDuration=!0}return n}({...s,velocity:-e.millisecondsToSeconds(s.velocity||0)}),f=m||0,g=c/(2*Math.sqrt(u*h)),y=o-a,v=e.millisecondsToSeconds(Math.sqrt(u/h)),b=Math.abs(y)<5;let T;if(i||(i=b?Mt.granular:Mt.default),r||(r=b?xt.granular:xt.default),g<1){const t=Dt(v,g);T=e=>{const n=Math.exp(-g*v*e);return o-n*((f+g*v*y)/t*Math.sin(t*e)+y*Math.cos(t*e))}}else if(1===g)T=t=>o-Math.exp(-v*t)*(y+(f+v*y)*t);else{const t=v*Math.sqrt(g*g-1);T=e=>{const n=Math.exp(-g*v*e),s=Math.min(t*e,300);return o-n*((f+g*v*y)*Math.sinh(s)+t*y*Math.cosh(s))/t}}const w={calculatedDuration:p&&d||null,next:t=>{const n=T(t);if(p)l.done=t>=d;else{let s=0===t?f:0;g<1&&(s=0===t?e.secondsToMilliseconds(f):pt(T,t,n));const a=Math.abs(s)<=i,u=Math.abs(o-n)<=r;l.done=a&&u}return l.value=l.done?o:n,l},toString:()=>{const t=Math.min(dt(w),ht),e=ct(e=>w.next(t*e).value,t,30);return t+"ms "+e},toTransition:()=>{}};return w}function Wt({keyframes:t,velocity:e=0,power:n=.8,timeConstant:s=325,bounceDamping:i=10,bounceStiffness:r=500,modifyTarget:a,min:o,max:l,restDelta:u=.5,restSpeed:c}){const h=t[0],d={done:!1,value:h},m=t=>void 0===o?l:void 0===l||Math.abs(o-t)-p*Math.exp(-t/s),v=t=>g+y(t),b=t=>{const e=y(t),n=v(t);d.done=Math.abs(e)<=u,d.value=d.done?g:n};let T,w;const M=t=>{var e;(e=d.value,void 0!==o&&el)&&(T=t,w=Kt({keyframes:[d.value,m(d.value)],velocity:pt(v,t,d.value),damping:i,stiffness:r,restDelta:u,restSpeed:c}))};return M(0),{calculatedDuration:null,next:t=>{let e=!1;return w||void 0!==T||(e=!0,b(t),M(t)),void 0!==T&&t>=T?w.next(t-T):(!e&&b(t),d)}}}function Lt(t,n,{clamp:s=!0,ease:i,mixer:r}={}){const a=t.length;if(e.invariant(a===n.length,"Both input and output ranges must be the same length"),1===a)return()=>n[0];if(2===a&&n[0]===n[1])return()=>n[1];const o=t[0]===t[1];t[0]>t[a-1]&&(t=[...t].reverse(),n=[...n].reverse());const l=function(t,n,s){const i=[],r=s||e.MotionGlobalConfig.mix||lt,a=t.length-1;for(let s=0;s{if(o&&s1)for(;ic(e.clamp(t[0],t[a-1],n)):c}function Bt(t,n){const s=t[t.length-1];for(let i=1;i<=n;i++){const r=e.progress(0,n,i);t.push(_(s,1,r))}}function $t(t){const e=[0];return Bt(e,t.length-1),e}function Nt(t,e){return t.map(t=>t*e)}function jt(t,n){return t.map(()=>n||e.easeInOut).splice(0,t.length-1)}function It({duration:t=300,keyframes:n,times:s,ease:i="easeInOut"}){const r=e.isEasingArray(i)?i.map(e.easingDefinitionToFunction):e.easingDefinitionToFunction(i),a={done:!1,value:n[0]},o=Lt(Nt(s&&s.length===n.length?s:$t(n),t),n,{ease:Array.isArray(r)?r:jt(n,r)});return{calculatedDuration:t,next:e=>(a.value=o(e),a.done=e>=t,a)}}Kt.applyToOptions=t=>{const n=mt(t,100,Kt);return t.ease=n.ease,t.duration=e.secondsToMilliseconds(n.duration),t.type="keyframes",t};const Yt=t=>null!==t;function zt(t,{repeat:e,repeatType:n="loop"},s,i=1){const r=t.filter(Yt),a=i<0||e&&"loop"!==n&&e%2==1?0:r.length-1;return a&&void 0!==s?s:r[a]}const Xt={decay:Wt,inertia:Wt,tween:It,keyframes:It,spring:Kt};function Ut(t){"string"==typeof t.type&&(t.type=Xt[t.type])}class Gt{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const qt=t=>t/100;class Zt extends Gt{constructor(t){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{const{motionValue:t}=this.options;t&&t.updatedAt!==h.now()&&this.tick(h.now()),this.isStopped=!0,"idle"!==this.state&&(this.teardown(),this.options.onStop?.())},d.mainThread++,this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;Ut(t);const{type:n=It,repeat:s=0,repeatDelay:i=0,repeatType:r,velocity:a=0}=t;let{keyframes:o}=t;const l=n||It;l!==It&&"number"!=typeof o[0]&&(this.mixKeyframes=e.pipe(qt,lt(o[0],o[1])),o=[0,100]);const u=l({...t,keyframes:o});"mirror"===r&&(this.mirroredGenerator=l({...t,keyframes:[...o].reverse(),velocity:-a})),null===u.calculatedDuration&&(u.calculatedDuration=dt(u));const{calculatedDuration:c}=u;this.calculatedDuration=c,this.resolvedDuration=c+i,this.totalDuration=this.resolvedDuration*(s+1)-i,this.generator=u}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,n=!1){const{generator:s,totalDuration:i,mixKeyframes:r,mirroredGenerator:a,resolvedDuration:o,calculatedDuration:l}=this;if(null===this.startTime)return s.next(0);const{delay:u=0,keyframes:c,repeat:h,repeatType:d,repeatDelay:m,type:p,onUpdate:f,finalKeyframe:g}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-i/this.speed,this.startTime)),n?this.currentTime=t:this.updateTime(t);const y=this.currentTime-u*(this.playbackSpeed>=0?1:-1),v=this.playbackSpeed>=0?y<0:y>i;this.currentTime=Math.max(y,0),"finished"===this.state&&null===this.holdTime&&(this.currentTime=i);let b=this.currentTime,T=s;if(h){const t=Math.min(this.currentTime,i)/o;let n=Math.floor(t),s=t%1;!s&&t>=1&&(s=1),1===s&&n--,n=Math.min(n,h+1);Boolean(n%2)&&("reverse"===d?(s=1-s,m&&(s-=m/o)):"mirror"===d&&(T=a)),b=e.clamp(0,1,s)*o}const w=v?{done:!1,value:c[0]}:T.next(b);r&&(w.value=r(w.value));let{done:M}=w;v||null===l||(M=this.playbackSpeed>=0?this.currentTime>=i:this.currentTime<=0);const x=null===this.holdTime&&("finished"===this.state||"running"===this.state&&M);return x&&p!==Wt&&(w.value=zt(c,this.options,g,this.speed)),f&&f(w.value),x&&this.finish(),w}then(t,e){return this.finished.then(t,e)}get duration(){return e.millisecondsToSeconds(this.calculatedDuration)}get time(){return e.millisecondsToSeconds(this.currentTime)}set time(t){t=e.secondsToMilliseconds(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),this.driver?.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(h.now());const n=this.playbackSpeed!==t;this.playbackSpeed=t,n&&(this.time=e.millisecondsToSeconds(this.currentTime))}play(){if(this.isStopped)return;const{driver:t=ut,startTime:e}=this.options;this.driver||(this.driver=t(t=>this.tick(t))),this.options.onPlay?.();const n=this.driver.now();"finished"===this.state?(this.updateFinished(),this.startTime=n):null!==this.holdTime?this.startTime=n-this.holdTime:this.startTime||(this.startTime=e??n),"finished"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(h.now()),this.holdTime=this.currentTime}complete(){"running"!==this.state&&this.play(),this.state="finished",this.holdTime=null}finish(){this.notifyFinished(),this.teardown(),this.state="finished",this.options.onComplete?.()}cancel(){this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),this.options.onCancel?.()}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null,d.mainThread--}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),this.driver?.stop(),t.observe(this)}}function _t(t){for(let e=1;e180*t/Math.PI,Jt=t=>{const e=Ht(Math.atan2(t[1],t[0]));return te(e)},Qt={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:Jt,rotateZ:Jt,skewX:t=>Ht(Math.atan(t[1])),skewY:t=>Ht(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},te=t=>((t%=360)<0&&(t+=360),t),ee=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),ne=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),se={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:ee,scaleY:ne,scale:t=>(ee(t)+ne(t))/2,rotateX:t=>te(Ht(Math.atan2(t[6],t[5]))),rotateY:t=>te(Ht(Math.atan2(-t[2],t[0]))),rotateZ:Jt,rotate:Jt,skewX:t=>Ht(Math.atan(t[4])),skewY:t=>Ht(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function ie(t){return t.includes("scale")?1:0}function re(t,e){if(!t||"none"===t)return ie(e);const n=t.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let s,i;if(n)s=se,i=n;else{const e=t.match(/^matrix\(([-\d.e\s,]+)\)$/u);s=Qt,i=e}if(!i)return ie(e);const r=s[e],a=i[1].split(",").map(ae);return"function"==typeof r?r(a):a[r]}function ae(t){return parseFloat(t.trim())}const oe=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],le=(()=>new Set(oe))(),ue=t=>t===v||t===F,ce=new Set(["x","y","z"]),he=oe.filter(t=>!ce.has(t));const de={width:({x:t},{paddingLeft:e="0",paddingRight:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e="0",paddingBottom:n="0"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>re(e,"x"),y:(t,{transform:e})=>re(e,"y")};de.translateX=de.x,de.translateY=de.y;const me=new Set;let pe=!1,fe=!1,ge=!1;function ye(){if(fe){const t=Array.from(me).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return he.forEach(n=>{const s=t.getValue(n);void 0!==s&&(e.push([n,s.get()]),s.set(n.startsWith("scale")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{t.getValue(e)?.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}fe=!1,pe=!1,me.forEach(t=>t.complete(ge)),me.clear()}function ve(){me.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(fe=!0)})}function be(){ge=!0,ve(),ye(),ge=!1}class Te{constructor(t,e,n,s,i,r=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=s,this.element=i,this.isAsync=r}scheduleResolve(){this.state="scheduled",this.isAsync?(me.add(this),pe||(pe=!0,r.read(ve),r.resolveKeyframes(ye))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:s}=this;if(null===t[0]){const i=s?.get(),r=t[t.length-1];if(void 0!==i)t[0]=i;else if(n&&e){const s=n.readValue(e,r);null!=s&&(t[0]=s)}void 0===t[0]&&(t[0]=r),s&&void 0===i&&s.set(t[0])}_t(t)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(t=!1){this.state="complete",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,t),me.delete(this)}cancel(){"scheduled"===this.state&&(me.delete(this),this.state="pending")}resume(){"pending"===this.state&&this.scheduleResolve()}}const we=t=>t.startsWith("--");function Me(t,e,n){we(e)?t.style.setProperty(e,n):t.style[e]=n}const xe=e.memo(()=>void 0!==window.ScrollTimeline),Se={};function Ae(t,n){const s=e.memo(t);return()=>Se[n]??s()}const ke=Ae(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch(t){return!1}return!0},"linearEasing"),Ee=([t,e,n,s])=>`cubic-bezier(${t}, ${e}, ${n}, ${s})`,Ve={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:Ee([0,.65,.55,1]),circOut:Ee([.55,0,1,.45]),backIn:Ee([.31,.01,.66,-.59]),backOut:Ee([.33,1.53,.69,.99])};function Pe(t,n){return t?"function"==typeof t?ke()?ct(t,n):"ease-out":e.isBezierDefinition(t)?Ee(t):Array.isArray(t)?t.map(t=>Pe(t,n)||Ve.easeOut):Ve[t]:void 0}function Re(t,e,n,{delay:i=0,duration:r=300,repeat:a=0,repeatType:o="loop",ease:l="easeOut",times:u}={},c=void 0){const h={[e]:n};u&&(h.offset=u);const m=Pe(l,r);Array.isArray(m)&&(h.easing=m),s.value&&d.waapi++;const p={delay:i,duration:r,easing:Array.isArray(m)?"linear":m,fill:"both",iterations:a+1,direction:"reverse"===o?"alternate":"normal"};c&&(p.pseudoElement=c);const f=t.animate(h,p);return s.value&&f.finished.finally(()=>{d.waapi--}),f}function De(t){return"function"==typeof t&&"applyToOptions"in t}function Fe({type:t,...e}){return De(t)&&ke()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}class Oe extends Gt{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,!t)return;const{element:n,name:s,keyframes:i,pseudoElement:r,allowFlatten:a=!1,finalKeyframe:o,onComplete:l}=t;this.isPseudoElement=Boolean(r),this.allowFlatten=a,this.options=t,e.invariant("string"!=typeof t.type,'animateMini doesn\'t support "type" as a string. Did you mean to import { spring } from "motion"?');const u=Fe(t);this.animation=Re(n,s,i,u,r),!1===u.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!r){const t=zt(i,this.options,o,this.speed);this.updateMotionValue?this.updateMotionValue(t):Me(n,s,t),this.animation.cancel()}l?.(),this.notifyFinished()}}play(){this.isStopped||(this.animation.play(),"finished"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){this.animation.finish?.()}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;"idle"!==t&&"finished"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){this.isPseudoElement||this.animation.commitStyles?.()}get duration(){const t=this.animation.effect?.getComputedTiming?.().duration||0;return e.millisecondsToSeconds(Number(t))}get time(){return e.millisecondsToSeconds(Number(this.animation.currentTime)||0)}set time(t){this.finishedTime=null,this.animation.currentTime=e.secondsToMilliseconds(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?"finished":this.animation.playState}get startTime(){return Number(this.animation.startTime)}set startTime(t){this.animation.startTime=t}attachTimeline({timeline:t,observe:n}){return this.allowFlatten&&this.animation.effect?.updateTiming({easing:"linear"}),this.animation.onfinish=null,t&&xe()?(this.animation.timeline=t,e.noop):n(this)}}const Ce={anticipate:e.anticipate,backInOut:e.backInOut,circInOut:e.circInOut};function Ke(t){"string"==typeof t.ease&&t.ease in Ce&&(t.ease=Ce[t.ease])}class We extends Oe{constructor(t){Ke(t),Ut(t),super(t),t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:n,onUpdate:s,onComplete:i,element:r,...a}=this.options;if(!n)return;if(void 0!==t)return void n.set(t);const o=new Zt({...a,autoplay:!1}),l=e.secondsToMilliseconds(this.finishedTime??this.time);n.setWithVelocity(o.sample(l-10).value,o.sample(l).value,10),o.stop()}}const Le=(t,e)=>"zIndex"!==e&&(!("number"!=typeof t&&!Array.isArray(t))||!("string"!=typeof t||!U.test(t)&&"0"!==t||t.startsWith("url(")));function Be(t){return e.isObject(t)&&"offsetHeight"in t}const $e=new Set(["opacity","clipPath","filter","transform"]),Ne=e.memo(()=>Object.hasOwnProperty.call(Element.prototype,"animate"));function je(t){const{motionValue:e,name:n,repeatDelay:s,repeatType:i,damping:r,type:a}=t;if(!Be(e?.owner?.current))return!1;const{onUpdate:o,transformTemplate:l}=e.owner.getProps();return Ne()&&n&&$e.has(n)&&("transform"!==n||!l)&&!o&&!s&&"mirror"!==i&&0!==r&&"inertia"!==a}class Ie{constructor(t){this.stop=()=>this.runAll("stop"),this.animations=t.filter(Boolean)}get finished(){return Promise.all(this.animations.map(t=>t.finished))}getAll(t){return this.animations[0][t]}setAll(t,e){for(let n=0;ne.attachTimeline(t));return()=>{e.forEach((t,e)=>{t&&t(),this.animations[e].stop()})}}get time(){return this.getAll("time")}set time(t){this.setAll("time",t)}get speed(){return this.getAll("speed")}set speed(t){this.setAll("speed",t)}get state(){return this.getAll("state")}get startTime(){return this.getAll("startTime")}get duration(){let t=0;for(let e=0;ee[t]())}play(){this.runAll("play")}pause(){this.runAll("pause")}cancel(){this.runAll("cancel")}complete(){this.runAll("complete")}}class Ye extends Oe{constructor(t){super(),this.animation=t,t.onfinish=()=>{this.finishedTime=this.time,this.notifyFinished()}}}const ze=new WeakMap;const Xe=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function Ue(t){const e=Xe.exec(t);if(!e)return[,];const[,n,s,i]=e;return[`--${n??s}`,i]}function Ge(t,n,s=1){e.invariant(s<=4,`Max CSS variable fallback depth detected in property "${t}". This may indicate a circular fallback dependency.`);const[i,r]=Ue(t);if(!i)return;const a=window.getComputedStyle(n).getPropertyValue(i);if(a){const t=a.trim();return e.isNumericalString(t)?parseFloat(t):t}return g(r)?Ge(r,n,s+1):r}function qe(t,e){return t?.[e]??t?.default??t}const Ze=new Set(["width","height","top","left","right","bottom",...oe]),_e=t=>e=>e.test(t),He=[v,F,D,R,C,O,{test:t=>"auto"===t,parse:t=>t}],Je=t=>He.find(_e(t));function Qe(t){return"number"==typeof t?0===t:null===t||("none"===t||"0"===t||e.isZeroValueString(t))}const tn=new Set(["brightness","contrast","saturate","opacity"]);function en(t){const[e,n]=t.slice(0,-1).split("(");if("drop-shadow"===e)return t;const[s]=n.match(M)||[];if(!s)return t;const i=n.replace(s,"");let r=tn.has(e)?1:0;return s!==n&&(r*=100),e+"("+r+i+")"}const nn=/\b([a-z-]*)\(.*?\)/gu,sn={...U,getAnimatableNone:t=>{const e=t.match(nn);return e?e.map(en).join(" "):t}},rn={...v,transform:Math.round},an={rotate:R,rotateX:R,rotateY:R,rotateZ:R,scale:T,scaleX:T,scaleY:T,scaleZ:T,skew:R,skewX:R,skewY:R,distance:F,translateX:F,translateY:F,translateZ:F,x:F,y:F,z:F,perspective:F,transformPerspective:F,opacity:b,originX:K,originY:K,originZ:F},on={borderWidth:F,borderTopWidth:F,borderRightWidth:F,borderBottomWidth:F,borderLeftWidth:F,borderRadius:F,radius:F,borderTopLeftRadius:F,borderTopRightRadius:F,borderBottomRightRadius:F,borderBottomLeftRadius:F,width:F,maxWidth:F,height:F,maxHeight:F,top:F,right:F,bottom:F,left:F,padding:F,paddingTop:F,paddingRight:F,paddingBottom:F,paddingLeft:F,margin:F,marginTop:F,marginRight:F,marginBottom:F,marginLeft:F,backgroundPositionX:F,backgroundPositionY:F,...an,zIndex:rn,fillOpacity:b,strokeOpacity:b,numOctaves:rn},ln={...on,color:L,backgroundColor:L,outlineColor:L,fill:L,stroke:L,borderColor:L,borderTopColor:L,borderRightColor:L,borderBottomColor:L,borderLeftColor:L,filter:sn,WebkitFilter:sn},un=t=>ln[t];function cn(t,e){let n=un(t);return n!==sn&&(n=U),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const hn=new Set(["auto","none","0"]);const dn=new Set(["borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderRadius","radius","borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius","width","maxWidth","height","maxHeight","top","right","bottom","left","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","margin","marginTop","marginRight","marginBottom","marginLeft","backgroundPositionX","backgroundPositionY"]);const mn=e.memo(()=>{try{document.createElement("div").animate({opacity:[1]})}catch(t){return!1}return!0}),pn=new Set(["opacity","clipPath","filter","transform"]);function fn(t,e,n){if(t instanceof EventTarget)return[t];if("string"==typeof t){let s=document;e&&(s=e.current);const i=n?.[t]??s.querySelectorAll(t);return i?Array.from(i):[]}return Array.from(t)}function gn(t){return(e,n)=>{const s=fn(e),i=[];for(const e of s){const s=t(e,n);i.push(s)}return()=>{for(const t of i)t()}}}const yn=(t,e)=>e&&"number"==typeof t?e.transform(t):t;class vn{constructor(){this.latest={},this.values=new Map}set(t,e,n,s,i=!0){const o=this.values.get(t);o&&o.onRemove();const l=()=>{const s=e.get();this.latest[t]=i?yn(s,on[t]):s,n&&r.render(n)};l();const u=e.on("change",l);s&&e.addDependent(s);const c=()=>{u(),n&&a(n),this.values.delete(t),s&&e.removeDependent(s)};return this.values.set(t,{value:e,onRemove:c}),c}get(t){return this.values.get(t)?.value}destroy(){for(const t of this.values.values())t.onRemove()}}function bn(t){const e=new WeakMap,n=[];return(s,i)=>{const r=e.get(s)??new vn;e.set(s,r);for(const e in i){const a=i[e],o=t(s,r,e,a);n.push(o)}return()=>{for(const t of n)t()}}}const Tn=(t,e,n,s)=>{const i=function(t,e){if(!(e in t))return!1;const n=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(t),e)||Object.getOwnPropertyDescriptor(t,e);return n&&"function"==typeof n.set}(t,n),r=i?n:n.startsWith("data")||n.startsWith("aria")?n.replace(/([A-Z])/g,t=>`-${t.toLowerCase()}`):n;const a=i?()=>{t[r]=e.latest[n]}:()=>{const s=e.latest[n];null==s?t.removeAttribute(r):t.setAttribute(r,String(s))};return e.set(n,s,a)},wn=gn(bn(Tn)),Mn=bn((t,e,n,s)=>e.set(n,s,()=>{t[n]=e.latest[n]},void 0,!1)),xn={current:void 0};class Sn{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const n=h.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=h.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,n){this.events[t]||(this.events[t]=new e.SubscriptionManager);const s=this.events[t].add(n);return"change"===t?()=>{s(),r.read(()=>{this.events.change.getSize()||this.stop()})}:s}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return xn.current&&xn.current.push(this),this.current}getPrevious(){return this.prev}getVelocity(){const t=h.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const n=Math.min(this.updatedAt-this.prevUpdatedAt,30);return e.velocityPerSecond(parseFloat(this.current)-parseFloat(this.prevFrameValue),n)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function An(t,e){return new Sn(t,e)}const kn={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"};const En=new Set(["originX","originY","originZ"]),Vn=(t,e,n,s)=>{let i,r;return le.has(n)?(e.get("transform")||(Be(t)||e.get("transformBox")||Vn(t,e,"transformBox",new Sn("fill-box")),e.set("transform",new Sn("none"),()=>{t.style.transform=function(t){let e="",n=!0;for(let s=0;s{const n=e.latest.originX??"50%",s=e.latest.originY??"50%",i=e.latest.originZ??0;t.style.transformOrigin=`${n} ${s} ${i}`}),r=e.get("transformOrigin")):i=we(n)?()=>{t.style.setProperty(n,e.latest[n])}:()=>{t.style[n]=e.latest[n]},e.set(n,s,i,r)},Pn=gn(bn(Vn)),Rn=F.transform;const Dn=gn(bn((t,e,n,s)=>{if(n.startsWith("path"))return function(t,e,n,s){return r.render(()=>t.setAttribute("pathLength","1")),"pathOffset"===n?e.set(n,s,()=>t.setAttribute("stroke-dashoffset",Rn(-e.latest[n]))):(e.get("stroke-dasharray")||e.set("stroke-dasharray",new Sn("1 1"),()=>{const{pathLength:n=1,pathSpacing:s}=e.latest;t.setAttribute("stroke-dasharray",`${Rn(n)} ${Rn(s??1-Number(n))}`)}),e.set(n,s,void 0,e.get("stroke-dasharray")))}(t,e,n,s);if(n.startsWith("attr"))return Tn(t,e,function(t){return t.replace(/^attr([A-Z])/,(t,e)=>e.toLowerCase())}(n),s);return(n in t.style?Vn:Tn)(t,e,n,s)}));const{schedule:Fn,cancel:On}=i(queueMicrotask,!1),Cn={x:!1,y:!1};function Kn(){return Cn.x||Cn.y}function Wn(t,e){const n=fn(t),s=new AbortController;return[n,{passive:!0,...e,signal:s.signal},()=>s.abort()]}function Ln(t){return!("touch"===t.pointerType||Kn())}const Bn=(t,e)=>!!e&&(t===e||Bn(t,e.parentElement)),$n=t=>"mouse"===t.pointerType?"number"!=typeof t.button||t.button<=0:!1!==t.isPrimary,Nn=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);const jn=new WeakSet;function In(t){return e=>{"Enter"===e.key&&t(e)}}function Yn(t,e){t.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}function zn(t){return $n(t)&&!Kn()}function Xn(t){return e.isObject(t)&&"ownerSVGElement"in t}const Un=new WeakMap;let Gn;const qn=(t,e,n)=>(s,i)=>i&&i[0]?i[0][t+"Size"]:Xn(s)&&"getBBox"in s?s.getBBox()[e]:s[n],Zn=qn("inline","width","offsetWidth"),_n=qn("block","height","offsetHeight");function Hn({target:t,borderBoxSize:e}){Un.get(t)?.forEach(n=>{n(t,{get width(){return Zn(t,e)},get height(){return _n(t,e)}})})}function Jn(t){t.forEach(Hn)}function Qn(t,e){Gn||"undefined"!=typeof ResizeObserver&&(Gn=new ResizeObserver(Jn));const n=fn(t);return n.forEach(t=>{let n=Un.get(t);n||(n=new Set,Un.set(t,n)),n.add(e),Gn?.observe(t)}),()=>{n.forEach(t=>{const n=Un.get(t);n?.delete(e),n?.size||Gn?.unobserve(t)})}}const ts=new Set;let es;function ns(t){return ts.add(t),es||(es=()=>{const t={get width(){return window.innerWidth},get height(){return window.innerHeight}};ts.forEach(e=>e(t))},window.addEventListener("resize",es)),()=>{ts.delete(t),ts.size||"function"!=typeof es||(window.removeEventListener("resize",es),es=void 0)}}function ss(){const{value:t}=s;null!==t?(t.frameloop.rate.push(o.delta),t.animations.mainThread.push(d.mainThread),t.animations.waapi.push(d.waapi),t.animations.layout.push(d.layout)):a(ss)}function is(t){return t.reduce((t,e)=>t+e,0)/t.length}function rs(t,e=is){return 0===t.length?{min:0,max:0,avg:0}:{min:Math.min(...t),max:Math.max(...t),avg:e(t)}}const as=t=>Math.round(1e3/t);function os(){s.value=null,s.addProjectionMetrics=null}function ls(){const{value:t}=s;if(!t)throw new Error("Stats are not being measured");os(),a(ss);const e={frameloop:{setup:rs(t.frameloop.setup),rate:rs(t.frameloop.rate),read:rs(t.frameloop.read),resolveKeyframes:rs(t.frameloop.resolveKeyframes),preUpdate:rs(t.frameloop.preUpdate),update:rs(t.frameloop.update),preRender:rs(t.frameloop.preRender),render:rs(t.frameloop.render),postRender:rs(t.frameloop.postRender)},animations:{mainThread:rs(t.animations.mainThread),waapi:rs(t.animations.waapi),layout:rs(t.animations.layout)},layoutProjection:{nodes:rs(t.layoutProjection.nodes),calculatedTargetDeltas:rs(t.layoutProjection.calculatedTargetDeltas),calculatedProjections:rs(t.layoutProjection.calculatedProjections)}},{rate:n}=e.frameloop;return n.min=as(n.min),n.max=as(n.max),n.avg=as(n.avg),[n.min,n.max]=[n.max,n.min],e}function us(t,e){if("first"===t)return 0;{const n=e-1;return"last"===t?n:n/2}}function cs(...t){const e=!Array.isArray(t[0]),n=e?0:-1,s=t[0+n],i=Lt(t[1+n],t[2+n],t[3+n]);return e?i(s):i}function hs(t){const e=[];xn.current=e;const n=t();xn.current=void 0;const s=An(n);return function(t,e,n){const s=()=>e.set(n()),i=()=>r.preRender(s,!1,!0),o=t.map(t=>t.on("change",i));e.on("destroy",()=>{o.forEach(t=>t()),a(s)})}(e,s,t),s}const ds=t=>Boolean(t&&t.getVelocity);function ms(t,e,n){const s=t.get();let i,a=null,o=s;const l="string"==typeof s?s.replace(/[\d.-]/g,""):void 0,u=()=>{a&&(a.stop(),a=null)},c=()=>{u(),a=new Zt({keyframes:[fs(t.get()),fs(o)],velocity:t.getVelocity(),type:"spring",restDelta:.001,restSpeed:.01,...n,onUpdate:i})};let h;return t.attach((e,n)=>(o=e,i=t=>n(ps(t,l)),r.postRender(c),t.get()),u),ds(e)&&(h=e.on("change",e=>t.set(ps(e,l))),t.on("destroy",h)),h}function ps(t,e){return e?t+e:t}function fs(t){return"number"==typeof t?t:parseFloat(t)}const gs=[...He,L,U];function ys(t){return"layout"===t?"group":"enter"===t||"new"===t?"new":"exit"===t||"old"===t?"old":"group"}let vs={},bs=null;const Ts=(t,e)=>{vs[t]=e},ws=()=>{bs||(bs=document.createElement("style"),bs.id="motion-view");let t="";for(const e in vs){const n=vs[e];t+=`${e} {\n`;for(const[e,s]of Object.entries(n))t+=` ${e}: ${s};\n`;t+="}\n"}bs.textContent=t,document.head.appendChild(bs),vs={}},Ms=()=>{bs&&bs.parentElement&&bs.parentElement.removeChild(bs)};function xs(t){const e=t.match(/::view-transition-(old|new|group|image-pair)\((.*?)\)/);return e?{layer:e[2],type:e[1]}:null}function Ss(t){const{effect:e}=t;return!!e&&(e.target===document.documentElement&&e.pseudoElement?.startsWith("::view-transition"))}const As=["layout","enter","exit","new","old"];function ks(t){const{update:n,targets:s,options:i}=t;if(!document.startViewTransition)return new Promise(async t=>{await n(),t(new Ie([]))});(function(t,e){return e.has(t)&&Object.keys(e.get(t)).length>0})("root",s)||Ts(":root",{"view-transition-name":"none"}),Ts("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)",{"animation-timing-function":"linear !important"}),ws();const r=document.startViewTransition(async()=>{await n()});return r.finished.finally(()=>{Ms()}),new Promise(t=>{r.ready.then(()=>{const n=document.getAnimations().filter(Ss),r=[];s.forEach((t,n)=>{for(const s of As){if(!t[s])continue;const{keyframes:a,options:o}=t[s];for(let[t,l]of Object.entries(a)){if(!l)continue;const a={...qe(i,t),...qe(o,t)},u=ys(s);if("opacity"===t&&!Array.isArray(l)){l=["new"===u?0:1,l]}"function"==typeof a.delay&&(a.delay=a.delay(0,1)),a.duration&&(a.duration=e.secondsToMilliseconds(a.duration)),a.delay&&(a.delay=e.secondsToMilliseconds(a.delay));const c=new Oe({...a,element:document.documentElement,name:t,pseudoElement:`::view-transition-${u}(${n})`,keyframes:l});r.push(c)}}});for(const t of n){if("finished"===t.playState)continue;const{effect:n}=t;if(!(n&&n instanceof KeyframeEffect))continue;const{pseudoElement:a}=n;if(!a)continue;const o=xs(a);if(!o)continue;const l=s.get(o.layer);if(l)Es(l,"enter")&&Es(l,"exit")&&n.getKeyframes().some(t=>t.mixBlendMode)?r.push(new Ye(t)):t.cancel();else{const s="group"===o.type?"layout":"";let a={...qe(i,s)};a.duration&&(a.duration=e.secondsToMilliseconds(a.duration)),a=Fe(a);const l=Pe(a.ease,a.duration);n.updateTiming({delay:e.secondsToMilliseconds(a.delay??0),duration:a.duration,easing:l}),r.push(new Ye(t))}}t(new Ie(r))})})}function Es(t,e){return t?.[e]?.keyframes.opacity}let Vs=[],Ps=null;function Rs(){Ps=null;const[t]=Vs;var n;t&&(n=t,e.removeItem(Vs,n),Ps=n,ks(n).then(t=>{n.notifyReady(t),t.finished.finally(Rs)}))}function Ds(){for(let t=Vs.length-1;t>=0;t--){const e=Vs[t],{interrupt:n}=e.options;if("immediate"===n){const n=Vs.slice(0,t+1).map(t=>t.update),s=Vs.slice(t+1);e.update=()=>{n.forEach(t=>t())},Vs=[e,...s];break}}Ps&&"immediate"!==Vs[0]?.options.interrupt||Rs()}class Fs{constructor(t,n={}){var s;this.currentSubject="root",this.targets=new Map,this.notifyReady=e.noop,this.readyPromise=new Promise(t=>{this.notifyReady=t}),this.update=t,this.options={interrupt:"wait",...n},s=this,Vs.push(s),Fn.render(Ds)}get(t){return this.currentSubject=t,this}layout(t,e){return this.updateTarget("layout",t,e),this}new(t,e){return this.updateTarget("new",t,e),this}old(t,e){return this.updateTarget("old",t,e),this}enter(t,e){return this.updateTarget("enter",t,e),this}exit(t,e){return this.updateTarget("exit",t,e),this}crossfade(t){return this.updateTarget("enter",{opacity:1},t),this.updateTarget("exit",{opacity:0},t),this}updateTarget(t,e,n={}){const{currentSubject:s,targets:i}=this;i.has(s)||i.set(s,{});i.get(s)[t]={keyframes:e,options:n}}then(t,e){return this.readyPromise.then(t,e)}}const Os=r,Cs=n.reduce((t,e)=>(t[e]=t=>a(t),t),{});t.AsyncMotionValueAnimation=class extends Gt{constructor({autoplay:t=!0,delay:e=0,type:n="keyframes",repeat:s=0,repeatDelay:i=0,repeatType:r="loop",keyframes:a,name:o,motionValue:l,element:u,...c}){super(),this.stop=()=>{this._animation&&(this._animation.stop(),this.stopTimeline?.()),this.keyframeResolver?.cancel()},this.createdAt=h.now();const d={autoplay:t,delay:e,type:n,repeat:s,repeatDelay:i,repeatType:r,name:o,motionValue:l,element:u,...c},m=u?.KeyframeResolver||Te;this.keyframeResolver=new m(a,(t,e,n)=>this.onKeyframesResolved(t,e,d,!n),o,l,u),this.keyframeResolver?.scheduleResolve()}onKeyframesResolved(t,n,s,i){this.keyframeResolver=void 0;const{name:r,type:a,velocity:o,delay:l,isHandoff:u,onUpdate:c}=s;this.resolvedAt=h.now(),function(t,n,s,i){const r=t[0];if(null===r)return!1;if("display"===n||"visibility"===n)return!0;const a=t[t.length-1],o=Le(r,n),l=Le(a,n);return e.warning(o===l,`You are trying to animate ${n} from "${r}" to "${a}". ${r} is not an animatable value - to enable this animation set ${r} to a value animatable to ${a} via the \`style\` property.`),!(!o||!l)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:n,...s,keyframes:t},m=!u&&je(d)?new We({...d,element:d.motionValue.owner.current}):new Zt(d);m.finished.then(()=>this.notifyFinished()).catch(e.noop),this.pendingTimeline&&(this.stopTimeline=m.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=m}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){return this._animation||(this.keyframeResolver?.resume(),be()),this._animation}get duration(){return this.animation.duration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){this._animation&&this.animation.cancel(),this.keyframeResolver?.cancel()}},t.DOMKeyframesResolver=class extends Te{constructor(t,e,n,s,i){super(t,e,n,s,i,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let n=0;n{t.getValue(e).set(n)}),this.resolveNoneKeyframes()}},t.GroupAnimation=Ie,t.GroupAnimationWithThen=class extends Ie{then(t,e){return this.finished.finally(t).then(()=>{})}},t.JSAnimation=Zt,t.KeyframeResolver=Te,t.MotionValue=Sn,t.NativeAnimation=Oe,t.NativeAnimationExtended=We,t.NativeAnimationWrapper=Ye,t.ViewTransitionBuilder=Fs,t.acceleratedValues=pn,t.activeAnimations=d,t.addAttrValue=Tn,t.addStyleValue=Vn,t.alpha=b,t.analyseComplexValue=I,t.animateValue=function(t){return new Zt(t)},t.animateView=function(t,e={}){return new Fs(t,e)},t.animationMapKey=(t,e="")=>`${t}:${e}`,t.applyPxDefaults=function(t,e){for(let n=0;ngs.find(_e(t)),t.flushKeyframeResolvers=be,t.frame=r,t.frameData=o,t.frameSteps=l,t.generateLinearEasing=ct,t.getAnimatableNone=cn,t.getAnimationMap=function(t){const e=ze.get(t)||new Map;return ze.set(t,e),e},t.getComputedStyle=function(t,e){const n=window.getComputedStyle(t);return we(e)?n.getPropertyValue(e):n[e]},t.getDefaultValueType=un,t.getMixer=it,t.getOriginIndex=us,t.getValueAsType=yn,t.getValueTransition=qe,t.getVariableValue=Ge,t.hex=V,t.hover=function(t,e,n={}){const[s,i,r]=Wn(t,n),a=t=>{if(!Ln(t))return;const{target:n}=t,s=e(n,t);if("function"!=typeof s||!n)return;const r=t=>{Ln(t)&&(s(t),n.removeEventListener("pointerleave",r))};n.addEventListener("pointerleave",r,i)};return s.forEach(t=>{t.addEventListener("pointerenter",a,i)}),r},t.hsla=W,t.hslaToRgba=q,t.inertia=Wt,t.interpolate=Lt,t.invisibleValues=et,t.isCSSVariableName=p,t.isCSSVariableToken=g,t.isDragActive=Kn,t.isDragging=Cn,t.isGenerator=De,t.isHTMLElement=Be,t.isMotionValue=ds,t.isNodeOrChild=Bn,t.isPrimaryPointer=$n,t.isSVGElement=Xn,t.isSVGSVGElement=function(t){return Xn(t)&&"svg"===t.tagName},t.isWaapiSupportedEasing=function t(n){return Boolean("function"==typeof n&&ke()||!n||"string"==typeof n&&(n in Ve||ke())||e.isBezierDefinition(n)||Array.isArray(n)&&n.every(t))},t.keyframes=It,t.mapEasingToNativeEasing=Pe,t.mapValue=function(t,e,n,s){const i=cs(e,n,s);return hs(()=>i(t.get()))},t.maxGeneratorDuration=ht,t.microtask=Fn,t.mix=lt,t.mixArray=rt,t.mixColor=tt,t.mixComplex=ot,t.mixImmediate=Z,t.mixLinearColor=H,t.mixNumber=_,t.mixObject=at,t.mixVisibility=nt,t.motionValue=An,t.number=v,t.numberValueTypes=on,t.observeTimeline=function(t,e){let n;const s=()=>{const{currentTime:s}=e,i=(null===s?0:s.value)/100;n!==i&&t(i),n=i};return r.preUpdate(s,!0),()=>a(s)},t.parseCSSVariable=Ue,t.parseValueFromTransform=re,t.percent=D,t.positionalKeys=Ze,t.press=function(t,e,n={}){const[s,i,r]=Wn(t,n),a=t=>{const s=t.currentTarget;if(!zn(t))return;jn.add(s);const r=e(s,t),a=(t,e)=>{window.removeEventListener("pointerup",o),window.removeEventListener("pointercancel",l),jn.has(s)&&jn.delete(s),zn(t)&&"function"==typeof r&&r(t,{success:e})},o=t=>{a(t,s===window||s===document||n.useGlobalTarget||Bn(s,t.target))},l=t=>{a(t,!1)};window.addEventListener("pointerup",o,i),window.addEventListener("pointercancel",l,i)};return s.forEach(t=>{var e;(n.useGlobalTarget?window:t).addEventListener("pointerdown",a,i),Be(t)&&(t.addEventListener("focus",t=>((t,e)=>{const n=t.currentTarget;if(!n)return;const s=In(()=>{if(jn.has(n))return;Yn(n,"down");const t=In(()=>{Yn(n,"up")});n.addEventListener("keyup",t,e),n.addEventListener("blur",()=>Yn(n,"cancel"),e)});n.addEventListener("keydown",s,e),n.addEventListener("blur",()=>n.removeEventListener("keydown",s),e)})(t,i)),e=t,Nn.has(e.tagName)||-1!==e.tabIndex||t.hasAttribute("tabindex")||(t.tabIndex=0))}),r},t.progressPercentage=K,t.propEffect=Mn,t.px=F,t.readTransformValue=(t,e)=>{const{transform:n="none"}=getComputedStyle(t);return re(n,e)},t.recordStats=function(){if(s.value)throw os(),new Error("Stats are already being measured");const t=s;return t.value={frameloop:{setup:[],rate:[],read:[],resolveKeyframes:[],preUpdate:[],update:[],preRender:[],render:[],postRender:[]},animations:{mainThread:[],waapi:[],layout:[]},layoutProjection:{nodes:[],calculatedTargetDeltas:[],calculatedProjections:[]}},t.addProjectionMetrics=e=>{const{layoutProjection:n}=t.value;n.nodes.push(e.nodes),n.calculatedTargetDeltas.push(e.calculatedTargetDeltas),n.calculatedProjections.push(e.calculatedProjections)},r.postRender(ss,!0),ls},t.resize=function(t,e){return"function"==typeof t?ns(t):Qn(t,e)},t.resolveElements=fn,t.rgbUnit=k,t.rgba=E,t.scale=T,t.setDragLock=function(t){return"x"===t||"y"===t?Cn[t]?null:(Cn[t]=!0,()=>{Cn[t]=!1}):Cn.x||Cn.y?null:(Cn.x=Cn.y=!0,()=>{Cn.x=Cn.y=!1})},t.setStyle=Me,t.spring=Kt,t.springValue=function(t,e){const n=An(ds(t)?t.get():t);return ms(n,t,e),n},t.stagger=function(t=.1,{startDelay:n=0,from:s=0,ease:i}={}){return(r,a)=>{const o="number"==typeof s?s:us(s,a),l=Math.abs(o-r);let u=t*l;if(i){const n=a*t;u=e.easingDefinitionToFunction(i)(u/n)*n}return n+u}},t.startWaapiAnimation=Re,t.statsBuffer=s,t.styleEffect=Pn,t.supportedWaapiEasing=Ve,t.supportsBrowserAnimation=je,t.supportsFlags=Se,t.supportsLinearEasing=ke,t.supportsPartialKeyframes=mn,t.supportsScrollTimeline=xe,t.svgEffect=Dn,t.sync=Os,t.testValueType=_e,t.time=h,t.transform=cs,t.transformPropOrder=oe,t.transformProps=le,t.transformValue=hs,t.transformValueTypes=an,t.vh=O,t.vw=C}); diff --git a/node_modules/motion-dom/dist/size-rollup-motion-value.js b/node_modules/motion-dom/dist/size-rollup-motion-value.js new file mode 100644 index 00000000..f850b5f1 --- /dev/null +++ b/node_modules/motion-dom/dist/size-rollup-motion-value.js @@ -0,0 +1 @@ +const t={};class e{constructor(){this.subscriptions=[]}add(t){var e,s;return e=this.subscriptions,s=t,-1===e.indexOf(s)&&e.push(s),()=>function(t,e){const s=t.indexOf(e);s>-1&&t.splice(s,1)}(this.subscriptions,t)}notify(t,e,s){const i=this.subscriptions.length;if(i)if(1===i)this.subscriptions[0](t,e,s);else for(let n=0;nr=!0,h=s.reduce((t,e)=>(t[e]=function(t,e){let s=new Set,n=new Set,r=!1,a=!1;const o=new WeakSet;let c={delta:0,timestamp:0,isProcessing:!1},h=0;function d(e){o.has(e)&&(p.schedule(e),t()),h++,e(c)}const p={schedule:(t,e=!1,i=!1)=>{const a=i&&r?s:n;return e&&o.add(t),a.has(t)||a.add(t),t},cancel:t=>{n.delete(t),o.delete(t)},process:t=>{c=t,r?a=!0:(r=!0,[s,n]=[n,s],s.forEach(d),e&&i.value&&i.value.frameloop[e].push(h),h=0,s.clear(),r=!1,a&&(a=!1,p.process(t)))}};return p}(c,n?e:void 0),t),{}),{setup:d,read:p,resolveKeyframes:u,preUpdate:l,update:v,preRender:m,render:f,postRender:g}=h,y=()=>{const s=t.useManualTiming?o.timestamp:performance.now();r=!1,t.useManualTiming||(o.delta=a?1e3/60:Math.max(Math.min(s-o.timestamp,40),1)),o.timestamp=s,o.isProcessing=!0,d.process(o),p.process(o),u.process(o),l.process(o),v.process(o),m.process(o),f.process(o),g.process(o),o.isProcessing=!1,r&&n&&(a=!1,e(y))};return{schedule:s.reduce((t,s)=>{const i=h[s];return t[s]=(t,s=!1,n=!1)=>(r||(r=!0,a=!0,o.isProcessing||e(y)),i.schedule(t,s,n)),t},{}),cancel:t=>{for(let e=0;et,!0);let h;function d(){h=void 0}const p={now:()=>(void 0===h&&p.set(o.isProcessing||t.useManualTiming?o.timestamp:performance.now()),h),set:t=>{h=t,queueMicrotask(d)}},u={current:void 0};class l{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const s=p.now();if(this.updatedAt!==s&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=p.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,s){this.events[t]||(this.events[t]=new e);const i=this.events[t].add(s);return"change"===t?()=>{i(),r.read(()=>{this.events.change.getSize()||this.stop()})}:i}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,s){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-s}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return u.current&&u.current.push(this),this.current}getPrevious(){return this.prev}getVelocity(){const t=p.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return s=parseFloat(this.current)-parseFloat(this.prevFrameValue),(i=e)?s*(1e3/i):0;var s,i}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function v(t,e){return new l(t,e)}export{l as MotionValue,u as collectMotionValues,v as motionValue}; diff --git a/node_modules/motion-dom/dist/size-rollup-style-effect.js b/node_modules/motion-dom/dist/size-rollup-style-effect.js new file mode 100644 index 00000000..c44ceea2 --- /dev/null +++ b/node_modules/motion-dom/dist/size-rollup-style-effect.js @@ -0,0 +1 @@ +const t=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],e=(()=>new Set(t))();const s={};class n{constructor(){this.subscriptions=[]}add(t){var e,s;return e=this.subscriptions,s=t,-1===e.indexOf(s)&&e.push(s),()=>function(t,e){const s=t.indexOf(e);s>-1&&t.splice(s,1)}(this.subscriptions,t)}notify(t,e,s){const n=this.subscriptions.length;if(n)if(1===n)this.subscriptions[0](t,e,s);else for(let r=0;rn=!0,h=r.reduce((t,s)=>(t[s]=function(t,e){let s=new Set,n=new Set,r=!1,o=!1;const a=new WeakSet;let c={delta:0,timestamp:0,isProcessing:!1},h=0;function d(e){a.has(e)&&(p.schedule(e),t()),h++,e(c)}const p={schedule:(t,e=!1,i=!1)=>{const o=i&&r?s:n;return e&&a.add(t),o.has(t)||o.add(t),t},cancel:t=>{n.delete(t),a.delete(t)},process:t=>{c=t,r?o=!0:(r=!0,[s,n]=[n,s],s.forEach(d),e&&i.value&&i.value.frameloop[e].push(h),h=0,s.clear(),r=!1,o&&(o=!1,p.process(t)))}};return p}(c,e?s:void 0),t),{}),{setup:d,read:p,resolveKeyframes:u,preUpdate:l,update:f,preRender:m,render:v,postRender:g}=h,y=()=>{const r=s.useManualTiming?a.timestamp:performance.now();n=!1,s.useManualTiming||(a.delta=o?1e3/60:Math.max(Math.min(r-a.timestamp,40),1)),a.timestamp=r,a.isProcessing=!0,d.process(a),p.process(a),u.process(a),l.process(a),f.process(a),m.process(a),v.process(a),g.process(a),a.isProcessing=!1,n&&e&&(o=!1,t(y))};return{schedule:r.reduce((e,s)=>{const r=h[s];return e[s]=(e,s=!1,i=!1)=>(n||(n=!0,o=!0,a.isProcessing||t(y)),r.schedule(e,s,i)),e},{}),cancel:t=>{for(let e=0;et,!0);let p;function u(){p=void 0}const l={now:()=>(void 0===p&&l.set(h.isProcessing||s.useManualTiming?h.timestamp:performance.now()),p),set:t=>{p=t,queueMicrotask(u)}};class f{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=(t,e=!0)=>{const s=l.now();if(this.updatedAt!==s&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(this.events.change?.notify(this.current),this.dependents))for(const t of this.dependents)t.dirty();e&&this.events.renderRequest?.notify(this.current)},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=l.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on("change",t)}on(t,e){this.events[t]||(this.events[t]=new n);const s=this.events[t].add(e);return"change"===t?()=>{s(),a.read(()=>{this.events.change.getSize()||this.stop()})}:s}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t,e=!0){e&&this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t,e)}setWithVelocity(t,e,s){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-s}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){this.events.change?.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=l.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return s=parseFloat(this.current)-parseFloat(this.prevFrameValue),(n=e)?s*(1e3/n):0;var s,n}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){this.dependents?.clear(),this.events.destroy?.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function m(t){return(e,s)=>{const n=function(t,e,s){if(t instanceof EventTarget)return[t];if("string"==typeof t){let n=document;e&&(n=e.current);const r=s?.[t]??n.querySelectorAll(t);return r?Array.from(r):[]}return Array.from(t)}(e),r=[];for(const e of n){const n=t(e,s);r.push(n)}return()=>{for(const t of r)t()}}}const v={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},g={...v,transform:t=>((t,e,s)=>s>e?e:s({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),A=b("deg"),P=b("%"),R=b("px"),k=(()=>({...P,parse:t=>P.parse(t)/100,transform:t=>P.transform(100*t)}))(),F={borderWidth:R,borderTopWidth:R,borderRightWidth:R,borderBottomWidth:R,borderLeftWidth:R,borderRadius:R,radius:R,borderTopLeftRadius:R,borderTopRightRadius:R,borderBottomRightRadius:R,borderBottomLeftRadius:R,width:R,maxWidth:R,height:R,maxHeight:R,top:R,right:R,bottom:R,left:R,padding:R,paddingTop:R,paddingRight:R,paddingBottom:R,paddingLeft:R,margin:R,marginTop:R,marginRight:R,marginBottom:R,marginLeft:R,backgroundPositionX:R,backgroundPositionY:R,...{rotate:A,rotateX:A,rotateY:A,rotateZ:A,scale:y,scaleX:y,scaleY:y,scaleZ:y,skew:A,skewX:A,skewY:A,distance:R,translateX:R,translateY:R,translateZ:R,x:R,y:R,z:R,perspective:R,transformPerspective:R,opacity:g,originX:k,originY:k,originZ:R},zIndex:w,fillOpacity:g,strokeOpacity:g,numOctaves:w};class x{constructor(){this.latest={},this.values=new Map}set(t,e,s,n,r=!0){const i=this.values.get(t);i&&i.onRemove();const o=()=>{const n=e.get();this.latest[t]=r?((t,e)=>e&&"number"==typeof t?e.transform(t):t)(n,F[t]):n,s&&a.render(s)};o();const h=e.on("change",o);n&&e.addDependent(n);const d=()=>{h(),s&&c(s),this.values.delete(t),n&&e.removeDependent(n)};return this.values.set(t,{value:e,onRemove:d}),d}get(t){return this.values.get(t)?.value}destroy(){for(const t of this.values.values())t.onRemove()}}function T(t){const e=new WeakMap,s=[];return(n,r)=>{const i=e.get(n)??new x;e.set(n,i);for(const e in r){const o=r[e],a=t(n,i,e,o);s.push(a)}return()=>{for(const t of s)t()}}}const V={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"};const X=new Set(["originX","originY","originZ"]),Y=(s,n,r,i)=>{let o,a;return e.has(r)?(n.get("transform")||(function(t){return"object"==typeof(e=t)&&null!==e&&"offsetHeight"in t;var e}(s)||n.get("transformBox")||Y(s,n,"transformBox",new f("fill-box")),n.set("transform",new f("none"),()=>{s.style.transform=function(e){let s="",n=!0;for(let r=0;r{const t=n.latest.originX??"50%",e=n.latest.originY??"50%",r=n.latest.originZ??0;s.style.transformOrigin=`${t} ${e} ${r}`}),a=n.get("transformOrigin")):o=r.startsWith("--")?()=>{s.style.setProperty(r,n.latest[r])}:()=>{s.style[r]=n.latest[r]},n.set(r,i,o,a)},W=m(T(Y));export{Y as addStyleValue,W as styleEffect}; diff --git a/node_modules/motion-dom/package.json b/node_modules/motion-dom/package.json new file mode 100644 index 00000000..04c9bb4b --- /dev/null +++ b/node_modules/motion-dom/package.json @@ -0,0 +1,40 @@ +{ + "name": "motion-dom", + "version": "12.22.0", + "author": "Matt Perry", + "license": "MIT", + "repository": "https://github.com/motiondivision/motion", + "main": "./dist/cjs/index.js", + "types": "./dist/index.d.ts", + "module": "./dist/es/index.mjs", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/cjs/index.js", + "import": "./dist/es/index.mjs", + "default": "./dist/cjs/index.js" + } + }, + "dependencies": { + "motion-utils": "^12.19.0" + }, + "scripts": { + "clean": "rm -rf types dist lib", + "build": "yarn clean && tsc -p . && rollup -c && node ./scripts/check-bundle.js", + "dev": "concurrently -c blue,red -n tsc,rollup --kill-others \"tsc --watch -p . --preserveWatchOutput\" \"rollup --config --watch --no-watch.clearScreen\"", + "test": "jest --config jest.config.json --max-workers=2", + "measure": "rollup -c ./rollup.size.config.mjs" + }, + "bundlesize": [ + { + "path": "./dist/size-rollup-style-effect.js", + "maxSize": "2.9 kB" + }, + { + "path": "./dist/size-rollup-motion-value.js", + "maxSize": "1.8 kB" + } + ], + "gitHead": "17b2e79a8f6ae3f8d15e884fef821bfdeafc31c4" +} diff --git a/node_modules/motion-utils/LICENSE.md b/node_modules/motion-utils/LICENSE.md new file mode 100644 index 00000000..81110442 --- /dev/null +++ b/node_modules/motion-utils/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 [Motion](https://motion.dev) B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/motion-utils/dist/cjs/index.js b/node_modules/motion-utils/dist/cjs/index.js new file mode 100644 index 00000000..fcb83fa3 --- /dev/null +++ b/node_modules/motion-utils/dist/cjs/index.js @@ -0,0 +1,340 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +function addUniqueItem(arr, item) { + if (arr.indexOf(item) === -1) + arr.push(item); +} +function removeItem(arr, item) { + const index = arr.indexOf(item); + if (index > -1) + arr.splice(index, 1); +} +// Adapted from array-move +function moveItem([...arr], fromIndex, toIndex) { + const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex; + if (startIndex >= 0 && startIndex < arr.length) { + const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex; + const [item] = arr.splice(fromIndex, 1); + arr.splice(endIndex, 0, item); + } + return arr; +} + +const clamp = (min, max, v) => { + if (v > max) + return max; + if (v < min) + return min; + return v; +}; + +exports.warning = () => { }; +exports.invariant = () => { }; +if (process.env.NODE_ENV !== "production") { + exports.warning = (check, message) => { + if (!check && typeof console !== "undefined") { + console.warn(message); + } + }; + exports.invariant = (check, message) => { + if (!check) { + throw new Error(message); + } + }; +} + +const MotionGlobalConfig = {}; + +/** + * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1" + */ +const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v); + +function isObject(value) { + return typeof value === "object" && value !== null; +} + +/** + * Check if the value is a zero value string like "0px" or "0%" + */ +const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v); + +/*#__NO_SIDE_EFFECTS__*/ +function memo(callback) { + let result; + return () => { + if (result === undefined) + result = callback(); + return result; + }; +} + +/*#__NO_SIDE_EFFECTS__*/ +const noop = (any) => any; + +/** + * Pipe + * Compose other transformers to run linearily + * pipe(min(20), max(40)) + * @param {...functions} transformers + * @return {function} + */ +const combineFunctions = (a, b) => (v) => b(a(v)); +const pipe = (...transformers) => transformers.reduce(combineFunctions); + +/* + Progress within given range + + Given a lower limit and an upper limit, we return the progress + (expressed as a number 0-1) represented by the given value, and + limit that progress to within 0-1. + + @param [number]: Lower limit + @param [number]: Upper limit + @param [number]: Value to find progress within given range + @return [number]: Progress of value within range as expressed 0-1 +*/ +/*#__NO_SIDE_EFFECTS__*/ +const progress = (from, to, value) => { + const toFromDifference = to - from; + return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; +}; + +class SubscriptionManager { + constructor() { + this.subscriptions = []; + } + add(handler) { + addUniqueItem(this.subscriptions, handler); + return () => removeItem(this.subscriptions, handler); + } + notify(a, b, c) { + const numSubscriptions = this.subscriptions.length; + if (!numSubscriptions) + return; + if (numSubscriptions === 1) { + /** + * If there's only a single handler we can just call it without invoking a loop. + */ + this.subscriptions[0](a, b, c); + } + else { + for (let i = 0; i < numSubscriptions; i++) { + /** + * Check whether the handler exists before firing as it's possible + * the subscriptions were modified during this loop running. + */ + const handler = this.subscriptions[i]; + handler && handler(a, b, c); + } + } + } + getSize() { + return this.subscriptions.length; + } + clear() { + this.subscriptions.length = 0; + } +} + +/** + * Converts seconds to milliseconds + * + * @param seconds - Time in seconds. + * @return milliseconds - Converted time in milliseconds. + */ +/*#__NO_SIDE_EFFECTS__*/ +const secondsToMilliseconds = (seconds) => seconds * 1000; +/*#__NO_SIDE_EFFECTS__*/ +const millisecondsToSeconds = (milliseconds) => milliseconds / 1000; + +/* + Convert velocity into velocity per second + + @param [number]: Unit per frame + @param [number]: Frame duration in ms +*/ +function velocityPerSecond(velocity, frameDuration) { + return frameDuration ? velocity * (1000 / frameDuration) : 0; +} + +const warned = new Set(); +function hasWarned(message) { + return warned.has(message); +} +function warnOnce(condition, message, element) { + if (condition || warned.has(message)) + return; + console.warn(message); + if (element) + console.warn(element); + warned.add(message); +} + +const wrap = (min, max, v) => { + const rangeSize = max - min; + return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min; +}; + +/* + Bezier function generator + This has been modified from Gaëtan Renaudeau's BezierEasing + https://github.com/gre/bezier-easing/blob/master/src/index.js + https://github.com/gre/bezier-easing/blob/master/LICENSE + + I've removed the newtonRaphsonIterate algo because in benchmarking it + wasn't noticeably faster than binarySubdivision, indeed removing it + usually improved times, depending on the curve. + I also removed the lookup table, as for the added bundle size and loop we're + only cutting ~4 or so subdivision iterations. I bumped the max iterations up + to 12 to compensate and this still tended to be faster for no perceivable + loss in accuracy. + Usage + const easeOut = cubicBezier(.17,.67,.83,.67); + const x = easeOut(0.5); // returns 0.627... +*/ +// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. +const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * + t; +const subdivisionPrecision = 0.0000001; +const subdivisionMaxIterations = 12; +function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) { + let currentX; + let currentT; + let i = 0; + do { + currentT = lowerBound + (upperBound - lowerBound) / 2.0; + currentX = calcBezier(currentT, mX1, mX2) - x; + if (currentX > 0.0) { + upperBound = currentT; + } + else { + lowerBound = currentT; + } + } while (Math.abs(currentX) > subdivisionPrecision && + ++i < subdivisionMaxIterations); + return currentT; +} +function cubicBezier(mX1, mY1, mX2, mY2) { + // If this is a linear gradient, return linear easing + if (mX1 === mY1 && mX2 === mY2) + return noop; + const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2); + // If animation is at start/end, return t without easing + return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2); +} + +// Accepts an easing function and returns a new one that outputs mirrored values for +// the second half of the animation. Turns easeIn into easeInOut. +const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2; + +// Accepts an easing function and returns a new one that outputs reversed values. +// Turns easeIn into easeOut. +const reverseEasing = (easing) => (p) => 1 - easing(1 - p); + +const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99); +const backIn = /*@__PURE__*/ reverseEasing(backOut); +const backInOut = /*@__PURE__*/ mirrorEasing(backIn); + +const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); + +const circIn = (p) => 1 - Math.sin(Math.acos(p)); +const circOut = reverseEasing(circIn); +const circInOut = mirrorEasing(circIn); + +const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1); +const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1); +const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1); + +function steps(numSteps, direction = "end") { + return (progress) => { + progress = + direction === "end" + ? Math.min(progress, 0.999) + : Math.max(progress, 0.001); + const expanded = progress * numSteps; + const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded); + return clamp(0, 1, rounded / numSteps); + }; +} + +const isEasingArray = (ease) => { + return Array.isArray(ease) && typeof ease[0] !== "number"; +}; + +function getEasingForSegment(easing, i) { + return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing; +} + +const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number"; + +const easingLookup = { + linear: noop, + easeIn, + easeInOut, + easeOut, + circIn, + circInOut, + circOut, + backIn, + backInOut, + backOut, + anticipate, +}; +const isValidEasing = (easing) => { + return typeof easing === "string"; +}; +const easingDefinitionToFunction = (definition) => { + if (isBezierDefinition(definition)) { + // If cubic bezier definition, create bezier curve + exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`); + const [x1, y1, x2, y2] = definition; + return cubicBezier(x1, y1, x2, y2); + } + else if (isValidEasing(definition)) { + // Else lookup from table + exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`); + return easingLookup[definition]; + } + return definition; +}; + +exports.MotionGlobalConfig = MotionGlobalConfig; +exports.SubscriptionManager = SubscriptionManager; +exports.addUniqueItem = addUniqueItem; +exports.anticipate = anticipate; +exports.backIn = backIn; +exports.backInOut = backInOut; +exports.backOut = backOut; +exports.circIn = circIn; +exports.circInOut = circInOut; +exports.circOut = circOut; +exports.clamp = clamp; +exports.cubicBezier = cubicBezier; +exports.easeIn = easeIn; +exports.easeInOut = easeInOut; +exports.easeOut = easeOut; +exports.easingDefinitionToFunction = easingDefinitionToFunction; +exports.getEasingForSegment = getEasingForSegment; +exports.hasWarned = hasWarned; +exports.isBezierDefinition = isBezierDefinition; +exports.isEasingArray = isEasingArray; +exports.isNumericalString = isNumericalString; +exports.isObject = isObject; +exports.isZeroValueString = isZeroValueString; +exports.memo = memo; +exports.millisecondsToSeconds = millisecondsToSeconds; +exports.mirrorEasing = mirrorEasing; +exports.moveItem = moveItem; +exports.noop = noop; +exports.pipe = pipe; +exports.progress = progress; +exports.removeItem = removeItem; +exports.reverseEasing = reverseEasing; +exports.secondsToMilliseconds = secondsToMilliseconds; +exports.steps = steps; +exports.velocityPerSecond = velocityPerSecond; +exports.warnOnce = warnOnce; +exports.wrap = wrap; diff --git a/node_modules/motion-utils/dist/es/array.mjs b/node_modules/motion-utils/dist/es/array.mjs new file mode 100644 index 00000000..29c6b678 --- /dev/null +++ b/node_modules/motion-utils/dist/es/array.mjs @@ -0,0 +1,21 @@ +function addUniqueItem(arr, item) { + if (arr.indexOf(item) === -1) + arr.push(item); +} +function removeItem(arr, item) { + const index = arr.indexOf(item); + if (index > -1) + arr.splice(index, 1); +} +// Adapted from array-move +function moveItem([...arr], fromIndex, toIndex) { + const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex; + if (startIndex >= 0 && startIndex < arr.length) { + const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex; + const [item] = arr.splice(fromIndex, 1); + arr.splice(endIndex, 0, item); + } + return arr; +} + +export { addUniqueItem, moveItem, removeItem }; diff --git a/node_modules/motion-utils/dist/es/clamp.mjs b/node_modules/motion-utils/dist/es/clamp.mjs new file mode 100644 index 00000000..de2f3594 --- /dev/null +++ b/node_modules/motion-utils/dist/es/clamp.mjs @@ -0,0 +1,9 @@ +const clamp = (min, max, v) => { + if (v > max) + return max; + if (v < min) + return min; + return v; +}; + +export { clamp }; diff --git a/node_modules/motion-utils/dist/es/easing/anticipate.mjs b/node_modules/motion-utils/dist/es/easing/anticipate.mjs new file mode 100644 index 00000000..1a01d100 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/anticipate.mjs @@ -0,0 +1,5 @@ +import { backIn } from './back.mjs'; + +const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); + +export { anticipate }; diff --git a/node_modules/motion-utils/dist/es/easing/back.mjs b/node_modules/motion-utils/dist/es/easing/back.mjs new file mode 100644 index 00000000..a39bff7a --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/back.mjs @@ -0,0 +1,9 @@ +import { cubicBezier } from './cubic-bezier.mjs'; +import { mirrorEasing } from './modifiers/mirror.mjs'; +import { reverseEasing } from './modifiers/reverse.mjs'; + +const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99); +const backIn = /*@__PURE__*/ reverseEasing(backOut); +const backInOut = /*@__PURE__*/ mirrorEasing(backIn); + +export { backIn, backInOut, backOut }; diff --git a/node_modules/motion-utils/dist/es/easing/circ.mjs b/node_modules/motion-utils/dist/es/easing/circ.mjs new file mode 100644 index 00000000..af9d2130 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/circ.mjs @@ -0,0 +1,8 @@ +import { mirrorEasing } from './modifiers/mirror.mjs'; +import { reverseEasing } from './modifiers/reverse.mjs'; + +const circIn = (p) => 1 - Math.sin(Math.acos(p)); +const circOut = reverseEasing(circIn); +const circInOut = mirrorEasing(circIn); + +export { circIn, circInOut, circOut }; diff --git a/node_modules/motion-utils/dist/es/easing/cubic-bezier.mjs b/node_modules/motion-utils/dist/es/easing/cubic-bezier.mjs new file mode 100644 index 00000000..10c8d293 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/cubic-bezier.mjs @@ -0,0 +1,51 @@ +import { noop } from '../noop.mjs'; + +/* + Bezier function generator + This has been modified from Gaëtan Renaudeau's BezierEasing + https://github.com/gre/bezier-easing/blob/master/src/index.js + https://github.com/gre/bezier-easing/blob/master/LICENSE + + I've removed the newtonRaphsonIterate algo because in benchmarking it + wasn't noticeably faster than binarySubdivision, indeed removing it + usually improved times, depending on the curve. + I also removed the lookup table, as for the added bundle size and loop we're + only cutting ~4 or so subdivision iterations. I bumped the max iterations up + to 12 to compensate and this still tended to be faster for no perceivable + loss in accuracy. + Usage + const easeOut = cubicBezier(.17,.67,.83,.67); + const x = easeOut(0.5); // returns 0.627... +*/ +// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. +const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * + t; +const subdivisionPrecision = 0.0000001; +const subdivisionMaxIterations = 12; +function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) { + let currentX; + let currentT; + let i = 0; + do { + currentT = lowerBound + (upperBound - lowerBound) / 2.0; + currentX = calcBezier(currentT, mX1, mX2) - x; + if (currentX > 0.0) { + upperBound = currentT; + } + else { + lowerBound = currentT; + } + } while (Math.abs(currentX) > subdivisionPrecision && + ++i < subdivisionMaxIterations); + return currentT; +} +function cubicBezier(mX1, mY1, mX2, mY2) { + // If this is a linear gradient, return linear easing + if (mX1 === mY1 && mX2 === mY2) + return noop; + const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2); + // If animation is at start/end, return t without easing + return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2); +} + +export { cubicBezier }; diff --git a/node_modules/motion-utils/dist/es/easing/ease.mjs b/node_modules/motion-utils/dist/es/easing/ease.mjs new file mode 100644 index 00000000..b83d50fd --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/ease.mjs @@ -0,0 +1,7 @@ +import { cubicBezier } from './cubic-bezier.mjs'; + +const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1); +const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1); +const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1); + +export { easeIn, easeInOut, easeOut }; diff --git a/node_modules/motion-utils/dist/es/easing/modifiers/mirror.mjs b/node_modules/motion-utils/dist/es/easing/modifiers/mirror.mjs new file mode 100644 index 00000000..e330e1f7 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/modifiers/mirror.mjs @@ -0,0 +1,5 @@ +// Accepts an easing function and returns a new one that outputs mirrored values for +// the second half of the animation. Turns easeIn into easeInOut. +const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2; + +export { mirrorEasing }; diff --git a/node_modules/motion-utils/dist/es/easing/modifiers/reverse.mjs b/node_modules/motion-utils/dist/es/easing/modifiers/reverse.mjs new file mode 100644 index 00000000..266e7eff --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/modifiers/reverse.mjs @@ -0,0 +1,5 @@ +// Accepts an easing function and returns a new one that outputs reversed values. +// Turns easeIn into easeOut. +const reverseEasing = (easing) => (p) => 1 - easing(1 - p); + +export { reverseEasing }; diff --git a/node_modules/motion-utils/dist/es/easing/steps.mjs b/node_modules/motion-utils/dist/es/easing/steps.mjs new file mode 100644 index 00000000..281ecfd5 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/steps.mjs @@ -0,0 +1,15 @@ +import { clamp } from '../clamp.mjs'; + +function steps(numSteps, direction = "end") { + return (progress) => { + progress = + direction === "end" + ? Math.min(progress, 0.999) + : Math.max(progress, 0.001); + const expanded = progress * numSteps; + const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded); + return clamp(0, 1, rounded / numSteps); + }; +} + +export { steps }; diff --git a/node_modules/motion-utils/dist/es/easing/utils/get-easing-for-segment.mjs b/node_modules/motion-utils/dist/es/easing/utils/get-easing-for-segment.mjs new file mode 100644 index 00000000..f724e437 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/utils/get-easing-for-segment.mjs @@ -0,0 +1,8 @@ +import { wrap } from '../../wrap.mjs'; +import { isEasingArray } from './is-easing-array.mjs'; + +function getEasingForSegment(easing, i) { + return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing; +} + +export { getEasingForSegment }; diff --git a/node_modules/motion-utils/dist/es/easing/utils/is-bezier-definition.mjs b/node_modules/motion-utils/dist/es/easing/utils/is-bezier-definition.mjs new file mode 100644 index 00000000..0847203c --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/utils/is-bezier-definition.mjs @@ -0,0 +1,3 @@ +const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number"; + +export { isBezierDefinition }; diff --git a/node_modules/motion-utils/dist/es/easing/utils/is-easing-array.mjs b/node_modules/motion-utils/dist/es/easing/utils/is-easing-array.mjs new file mode 100644 index 00000000..365d58f0 --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/utils/is-easing-array.mjs @@ -0,0 +1,5 @@ +const isEasingArray = (ease) => { + return Array.isArray(ease) && typeof ease[0] !== "number"; +}; + +export { isEasingArray }; diff --git a/node_modules/motion-utils/dist/es/easing/utils/map.mjs b/node_modules/motion-utils/dist/es/easing/utils/map.mjs new file mode 100644 index 00000000..07a7f60c --- /dev/null +++ b/node_modules/motion-utils/dist/es/easing/utils/map.mjs @@ -0,0 +1,41 @@ +import { invariant } from '../../errors.mjs'; +import { noop } from '../../noop.mjs'; +import { anticipate } from '../anticipate.mjs'; +import { backIn, backInOut, backOut } from '../back.mjs'; +import { circIn, circInOut, circOut } from '../circ.mjs'; +import { cubicBezier } from '../cubic-bezier.mjs'; +import { easeIn, easeInOut, easeOut } from '../ease.mjs'; +import { isBezierDefinition } from './is-bezier-definition.mjs'; + +const easingLookup = { + linear: noop, + easeIn, + easeInOut, + easeOut, + circIn, + circInOut, + circOut, + backIn, + backInOut, + backOut, + anticipate, +}; +const isValidEasing = (easing) => { + return typeof easing === "string"; +}; +const easingDefinitionToFunction = (definition) => { + if (isBezierDefinition(definition)) { + // If cubic bezier definition, create bezier curve + invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`); + const [x1, y1, x2, y2] = definition; + return cubicBezier(x1, y1, x2, y2); + } + else if (isValidEasing(definition)) { + // Else lookup from table + invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`); + return easingLookup[definition]; + } + return definition; +}; + +export { easingDefinitionToFunction }; diff --git a/node_modules/motion-utils/dist/es/errors.mjs b/node_modules/motion-utils/dist/es/errors.mjs new file mode 100644 index 00000000..2b94228f --- /dev/null +++ b/node_modules/motion-utils/dist/es/errors.mjs @@ -0,0 +1,16 @@ +let warning = () => { }; +let invariant = () => { }; +if (process.env.NODE_ENV !== "production") { + warning = (check, message) => { + if (!check && typeof console !== "undefined") { + console.warn(message); + } + }; + invariant = (check, message) => { + if (!check) { + throw new Error(message); + } + }; +} + +export { invariant, warning }; diff --git a/node_modules/motion-utils/dist/es/global-config.mjs b/node_modules/motion-utils/dist/es/global-config.mjs new file mode 100644 index 00000000..5ecfe762 --- /dev/null +++ b/node_modules/motion-utils/dist/es/global-config.mjs @@ -0,0 +1,3 @@ +const MotionGlobalConfig = {}; + +export { MotionGlobalConfig }; diff --git a/node_modules/motion-utils/dist/es/index.mjs b/node_modules/motion-utils/dist/es/index.mjs new file mode 100644 index 00000000..56ea6c85 --- /dev/null +++ b/node_modules/motion-utils/dist/es/index.mjs @@ -0,0 +1,28 @@ +export { addUniqueItem, moveItem, removeItem } from './array.mjs'; +export { clamp } from './clamp.mjs'; +export { invariant, warning } from './errors.mjs'; +export { MotionGlobalConfig } from './global-config.mjs'; +export { isNumericalString } from './is-numerical-string.mjs'; +export { isObject } from './is-object.mjs'; +export { isZeroValueString } from './is-zero-value-string.mjs'; +export { memo } from './memo.mjs'; +export { noop } from './noop.mjs'; +export { pipe } from './pipe.mjs'; +export { progress } from './progress.mjs'; +export { SubscriptionManager } from './subscription-manager.mjs'; +export { millisecondsToSeconds, secondsToMilliseconds } from './time-conversion.mjs'; +export { velocityPerSecond } from './velocity-per-second.mjs'; +export { hasWarned, warnOnce } from './warn-once.mjs'; +export { wrap } from './wrap.mjs'; +export { anticipate } from './easing/anticipate.mjs'; +export { backIn, backInOut, backOut } from './easing/back.mjs'; +export { circIn, circInOut, circOut } from './easing/circ.mjs'; +export { cubicBezier } from './easing/cubic-bezier.mjs'; +export { easeIn, easeInOut, easeOut } from './easing/ease.mjs'; +export { mirrorEasing } from './easing/modifiers/mirror.mjs'; +export { reverseEasing } from './easing/modifiers/reverse.mjs'; +export { steps } from './easing/steps.mjs'; +export { getEasingForSegment } from './easing/utils/get-easing-for-segment.mjs'; +export { isBezierDefinition } from './easing/utils/is-bezier-definition.mjs'; +export { isEasingArray } from './easing/utils/is-easing-array.mjs'; +export { easingDefinitionToFunction } from './easing/utils/map.mjs'; diff --git a/node_modules/motion-utils/dist/es/is-numerical-string.mjs b/node_modules/motion-utils/dist/es/is-numerical-string.mjs new file mode 100644 index 00000000..1f2af969 --- /dev/null +++ b/node_modules/motion-utils/dist/es/is-numerical-string.mjs @@ -0,0 +1,6 @@ +/** + * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1" + */ +const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v); + +export { isNumericalString }; diff --git a/node_modules/motion-utils/dist/es/is-object.mjs b/node_modules/motion-utils/dist/es/is-object.mjs new file mode 100644 index 00000000..ebc709c4 --- /dev/null +++ b/node_modules/motion-utils/dist/es/is-object.mjs @@ -0,0 +1,5 @@ +function isObject(value) { + return typeof value === "object" && value !== null; +} + +export { isObject }; diff --git a/node_modules/motion-utils/dist/es/is-zero-value-string.mjs b/node_modules/motion-utils/dist/es/is-zero-value-string.mjs new file mode 100644 index 00000000..c1d5d1a5 --- /dev/null +++ b/node_modules/motion-utils/dist/es/is-zero-value-string.mjs @@ -0,0 +1,6 @@ +/** + * Check if the value is a zero value string like "0px" or "0%" + */ +const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v); + +export { isZeroValueString }; diff --git a/node_modules/motion-utils/dist/es/memo.mjs b/node_modules/motion-utils/dist/es/memo.mjs new file mode 100644 index 00000000..2c673915 --- /dev/null +++ b/node_modules/motion-utils/dist/es/memo.mjs @@ -0,0 +1,11 @@ +/*#__NO_SIDE_EFFECTS__*/ +function memo(callback) { + let result; + return () => { + if (result === undefined) + result = callback(); + return result; + }; +} + +export { memo }; diff --git a/node_modules/motion-utils/dist/es/noop.mjs b/node_modules/motion-utils/dist/es/noop.mjs new file mode 100644 index 00000000..26d7dd7d --- /dev/null +++ b/node_modules/motion-utils/dist/es/noop.mjs @@ -0,0 +1,4 @@ +/*#__NO_SIDE_EFFECTS__*/ +const noop = (any) => any; + +export { noop }; diff --git a/node_modules/motion-utils/dist/es/pipe.mjs b/node_modules/motion-utils/dist/es/pipe.mjs new file mode 100644 index 00000000..49156c1c --- /dev/null +++ b/node_modules/motion-utils/dist/es/pipe.mjs @@ -0,0 +1,11 @@ +/** + * Pipe + * Compose other transformers to run linearily + * pipe(min(20), max(40)) + * @param {...functions} transformers + * @return {function} + */ +const combineFunctions = (a, b) => (v) => b(a(v)); +const pipe = (...transformers) => transformers.reduce(combineFunctions); + +export { pipe }; diff --git a/node_modules/motion-utils/dist/es/progress.mjs b/node_modules/motion-utils/dist/es/progress.mjs new file mode 100644 index 00000000..b0a6e1e5 --- /dev/null +++ b/node_modules/motion-utils/dist/es/progress.mjs @@ -0,0 +1,19 @@ +/* + Progress within given range + + Given a lower limit and an upper limit, we return the progress + (expressed as a number 0-1) represented by the given value, and + limit that progress to within 0-1. + + @param [number]: Lower limit + @param [number]: Upper limit + @param [number]: Value to find progress within given range + @return [number]: Progress of value within range as expressed 0-1 +*/ +/*#__NO_SIDE_EFFECTS__*/ +const progress = (from, to, value) => { + const toFromDifference = to - from; + return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; +}; + +export { progress }; diff --git a/node_modules/motion-utils/dist/es/subscription-manager.mjs b/node_modules/motion-utils/dist/es/subscription-manager.mjs new file mode 100644 index 00000000..1bde8580 --- /dev/null +++ b/node_modules/motion-utils/dist/es/subscription-manager.mjs @@ -0,0 +1,40 @@ +import { addUniqueItem, removeItem } from './array.mjs'; + +class SubscriptionManager { + constructor() { + this.subscriptions = []; + } + add(handler) { + addUniqueItem(this.subscriptions, handler); + return () => removeItem(this.subscriptions, handler); + } + notify(a, b, c) { + const numSubscriptions = this.subscriptions.length; + if (!numSubscriptions) + return; + if (numSubscriptions === 1) { + /** + * If there's only a single handler we can just call it without invoking a loop. + */ + this.subscriptions[0](a, b, c); + } + else { + for (let i = 0; i < numSubscriptions; i++) { + /** + * Check whether the handler exists before firing as it's possible + * the subscriptions were modified during this loop running. + */ + const handler = this.subscriptions[i]; + handler && handler(a, b, c); + } + } + } + getSize() { + return this.subscriptions.length; + } + clear() { + this.subscriptions.length = 0; + } +} + +export { SubscriptionManager }; diff --git a/node_modules/motion-utils/dist/es/time-conversion.mjs b/node_modules/motion-utils/dist/es/time-conversion.mjs new file mode 100644 index 00000000..c059237a --- /dev/null +++ b/node_modules/motion-utils/dist/es/time-conversion.mjs @@ -0,0 +1,12 @@ +/** + * Converts seconds to milliseconds + * + * @param seconds - Time in seconds. + * @return milliseconds - Converted time in milliseconds. + */ +/*#__NO_SIDE_EFFECTS__*/ +const secondsToMilliseconds = (seconds) => seconds * 1000; +/*#__NO_SIDE_EFFECTS__*/ +const millisecondsToSeconds = (milliseconds) => milliseconds / 1000; + +export { millisecondsToSeconds, secondsToMilliseconds }; diff --git a/node_modules/motion-utils/dist/es/velocity-per-second.mjs b/node_modules/motion-utils/dist/es/velocity-per-second.mjs new file mode 100644 index 00000000..739a128b --- /dev/null +++ b/node_modules/motion-utils/dist/es/velocity-per-second.mjs @@ -0,0 +1,11 @@ +/* + Convert velocity into velocity per second + + @param [number]: Unit per frame + @param [number]: Frame duration in ms +*/ +function velocityPerSecond(velocity, frameDuration) { + return frameDuration ? velocity * (1000 / frameDuration) : 0; +} + +export { velocityPerSecond }; diff --git a/node_modules/motion-utils/dist/es/warn-once.mjs b/node_modules/motion-utils/dist/es/warn-once.mjs new file mode 100644 index 00000000..90b5dbbe --- /dev/null +++ b/node_modules/motion-utils/dist/es/warn-once.mjs @@ -0,0 +1,14 @@ +const warned = new Set(); +function hasWarned(message) { + return warned.has(message); +} +function warnOnce(condition, message, element) { + if (condition || warned.has(message)) + return; + console.warn(message); + if (element) + console.warn(element); + warned.add(message); +} + +export { hasWarned, warnOnce }; diff --git a/node_modules/motion-utils/dist/es/wrap.mjs b/node_modules/motion-utils/dist/es/wrap.mjs new file mode 100644 index 00000000..039f3427 --- /dev/null +++ b/node_modules/motion-utils/dist/es/wrap.mjs @@ -0,0 +1,6 @@ +const wrap = (min, max, v) => { + const rangeSize = max - min; + return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min; +}; + +export { wrap }; diff --git a/node_modules/motion-utils/dist/index.d.ts b/node_modules/motion-utils/dist/index.d.ts new file mode 100644 index 00000000..a73ee8a5 --- /dev/null +++ b/node_modules/motion-utils/dist/index.d.ts @@ -0,0 +1,140 @@ +declare function addUniqueItem(arr: T[], item: T): void; +declare function removeItem(arr: T[], item: T): void; +declare function moveItem([...arr]: T[], fromIndex: number, toIndex: number): T[]; + +declare const clamp: (min: number, max: number, v: number) => number; + +type DevMessage = (check: boolean, message: string) => void; +declare let warning: DevMessage; +declare let invariant: DevMessage; + +declare const MotionGlobalConfig: { + skipAnimations?: boolean; + instantAnimations?: boolean; + useManualTiming?: boolean; + WillChange?: any; + mix?: (a: T, b: T) => (p: number) => T; +}; + +/** + * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1" + */ +declare const isNumericalString: (v: string) => boolean; + +declare function isObject(value: unknown): value is object; + +/** + * Check if the value is a zero value string like "0px" or "0%" + */ +declare const isZeroValueString: (v: string) => boolean; + +declare function memo(callback: () => T): () => T; + +declare const noop: (any: T) => T; + +declare const pipe: (...transformers: Function[]) => Function; + +declare const progress: (from: number, to: number, value: number) => number; + +type GenericHandler = (...args: any) => void; +declare class SubscriptionManager { + private subscriptions; + add(handler: Handler): VoidFunction; + notify(a?: Parameters[0], b?: Parameters[1], c?: Parameters[2]): void; + getSize(): number; + clear(): void; +} + +/** + * Converts seconds to milliseconds + * + * @param seconds - Time in seconds. + * @return milliseconds - Converted time in milliseconds. + */ +declare const secondsToMilliseconds: (seconds: number) => number; +declare const millisecondsToSeconds: (milliseconds: number) => number; + +declare function velocityPerSecond(velocity: number, frameDuration: number): number; + +declare function hasWarned(message: string): boolean; +declare function warnOnce(condition: boolean, message: string, element?: Element): void; + +declare const wrap: (min: number, max: number, v: number) => number; + +declare const anticipate: (p: number) => number; + +declare const backOut: (t: number) => number; +declare const backIn: EasingFunction; +declare const backInOut: EasingFunction; + +type EasingFunction = (v: number) => number; +type EasingModifier = (easing: EasingFunction) => EasingFunction; +type BezierDefinition = readonly [number, number, number, number]; +type EasingDefinition = BezierDefinition | "linear" | "easeIn" | "easeOut" | "easeInOut" | "circIn" | "circOut" | "circInOut" | "backIn" | "backOut" | "backInOut" | "anticipate"; +/** + * The easing function to use. Set as one of: + * + * - The name of an in-built easing function. + * - An array of four numbers to define a cubic bezier curve. + * - An easing function, that accepts and returns a progress value between `0` and `1`. + * + * @public + */ +type Easing = EasingDefinition | EasingFunction; + +declare const circIn: EasingFunction; +declare const circOut: EasingFunction; +declare const circInOut: EasingFunction; + +declare function cubicBezier(mX1: number, mY1: number, mX2: number, mY2: number): (t: number) => number; + +declare const easeIn: (t: number) => number; +declare const easeOut: (t: number) => number; +declare const easeInOut: (t: number) => number; + +declare const mirrorEasing: EasingModifier; + +declare const reverseEasing: EasingModifier; + +type Direction = "start" | "end"; +declare function steps(numSteps: number, direction?: Direction): EasingFunction; + +declare function getEasingForSegment(easing: Easing | Easing[], i: number): Easing; + +declare const isBezierDefinition: (easing: Easing | Easing[]) => easing is BezierDefinition; + +declare const isEasingArray: (ease: any) => ease is Easing[]; + +declare const easingDefinitionToFunction: (definition: Easing) => EasingFunction; + +interface Point { + x: number; + y: number; +} +interface Axis { + min: number; + max: number; +} +interface Box { + x: Axis; + y: Axis; +} +interface BoundingBox { + top: number; + right: number; + bottom: number; + left: number; +} +interface AxisDelta { + translate: number; + scale: number; + origin: number; + originPoint: number; +} +interface Delta { + x: AxisDelta; + y: AxisDelta; +} +type TransformPoint = (point: Point) => Point; + +export { type Axis, type AxisDelta, type BezierDefinition, type BoundingBox, type Box, type Delta, type DevMessage, type Direction, type Easing, type EasingDefinition, type EasingFunction, type EasingModifier, MotionGlobalConfig, type Point, SubscriptionManager, type TransformPoint, addUniqueItem, anticipate, backIn, backInOut, backOut, circIn, circInOut, circOut, clamp, cubicBezier, easeIn, easeInOut, easeOut, easingDefinitionToFunction, getEasingForSegment, hasWarned, invariant, isBezierDefinition, isEasingArray, isNumericalString, isObject, isZeroValueString, memo, millisecondsToSeconds, mirrorEasing, moveItem, noop, pipe, progress, removeItem, reverseEasing, secondsToMilliseconds, steps, velocityPerSecond, warnOnce, warning, wrap }; diff --git a/node_modules/motion-utils/dist/motion-utils.dev.js b/node_modules/motion-utils/dist/motion-utils.dev.js new file mode 100644 index 00000000..37a3c4cf --- /dev/null +++ b/node_modules/motion-utils/dist/motion-utils.dev.js @@ -0,0 +1,344 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MotionUtils = {})); +})(this, (function (exports) { 'use strict'; + + function addUniqueItem(arr, item) { + if (arr.indexOf(item) === -1) + arr.push(item); + } + function removeItem(arr, item) { + const index = arr.indexOf(item); + if (index > -1) + arr.splice(index, 1); + } + // Adapted from array-move + function moveItem([...arr], fromIndex, toIndex) { + const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex; + if (startIndex >= 0 && startIndex < arr.length) { + const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex; + const [item] = arr.splice(fromIndex, 1); + arr.splice(endIndex, 0, item); + } + return arr; + } + + const clamp = (min, max, v) => { + if (v > max) + return max; + if (v < min) + return min; + return v; + }; + + exports.warning = () => { }; + exports.invariant = () => { }; + { + exports.warning = (check, message) => { + if (!check && typeof console !== "undefined") { + console.warn(message); + } + }; + exports.invariant = (check, message) => { + if (!check) { + throw new Error(message); + } + }; + } + + const MotionGlobalConfig = {}; + + /** + * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1" + */ + const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v); + + function isObject(value) { + return typeof value === "object" && value !== null; + } + + /** + * Check if the value is a zero value string like "0px" or "0%" + */ + const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v); + + /*#__NO_SIDE_EFFECTS__*/ + function memo(callback) { + let result; + return () => { + if (result === undefined) + result = callback(); + return result; + }; + } + + /*#__NO_SIDE_EFFECTS__*/ + const noop = (any) => any; + + /** + * Pipe + * Compose other transformers to run linearily + * pipe(min(20), max(40)) + * @param {...functions} transformers + * @return {function} + */ + const combineFunctions = (a, b) => (v) => b(a(v)); + const pipe = (...transformers) => transformers.reduce(combineFunctions); + + /* + Progress within given range + + Given a lower limit and an upper limit, we return the progress + (expressed as a number 0-1) represented by the given value, and + limit that progress to within 0-1. + + @param [number]: Lower limit + @param [number]: Upper limit + @param [number]: Value to find progress within given range + @return [number]: Progress of value within range as expressed 0-1 + */ + /*#__NO_SIDE_EFFECTS__*/ + const progress = (from, to, value) => { + const toFromDifference = to - from; + return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; + }; + + class SubscriptionManager { + constructor() { + this.subscriptions = []; + } + add(handler) { + addUniqueItem(this.subscriptions, handler); + return () => removeItem(this.subscriptions, handler); + } + notify(a, b, c) { + const numSubscriptions = this.subscriptions.length; + if (!numSubscriptions) + return; + if (numSubscriptions === 1) { + /** + * If there's only a single handler we can just call it without invoking a loop. + */ + this.subscriptions[0](a, b, c); + } + else { + for (let i = 0; i < numSubscriptions; i++) { + /** + * Check whether the handler exists before firing as it's possible + * the subscriptions were modified during this loop running. + */ + const handler = this.subscriptions[i]; + handler && handler(a, b, c); + } + } + } + getSize() { + return this.subscriptions.length; + } + clear() { + this.subscriptions.length = 0; + } + } + + /** + * Converts seconds to milliseconds + * + * @param seconds - Time in seconds. + * @return milliseconds - Converted time in milliseconds. + */ + /*#__NO_SIDE_EFFECTS__*/ + const secondsToMilliseconds = (seconds) => seconds * 1000; + /*#__NO_SIDE_EFFECTS__*/ + const millisecondsToSeconds = (milliseconds) => milliseconds / 1000; + + /* + Convert velocity into velocity per second + + @param [number]: Unit per frame + @param [number]: Frame duration in ms + */ + function velocityPerSecond(velocity, frameDuration) { + return frameDuration ? velocity * (1000 / frameDuration) : 0; + } + + const warned = new Set(); + function hasWarned(message) { + return warned.has(message); + } + function warnOnce(condition, message, element) { + if (condition || warned.has(message)) + return; + console.warn(message); + if (element) + console.warn(element); + warned.add(message); + } + + const wrap = (min, max, v) => { + const rangeSize = max - min; + return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min; + }; + + /* + Bezier function generator + This has been modified from Gaëtan Renaudeau's BezierEasing + https://github.com/gre/bezier-easing/blob/master/src/index.js + https://github.com/gre/bezier-easing/blob/master/LICENSE + + I've removed the newtonRaphsonIterate algo because in benchmarking it + wasn't noticeably faster than binarySubdivision, indeed removing it + usually improved times, depending on the curve. + I also removed the lookup table, as for the added bundle size and loop we're + only cutting ~4 or so subdivision iterations. I bumped the max iterations up + to 12 to compensate and this still tended to be faster for no perceivable + loss in accuracy. + Usage + const easeOut = cubicBezier(.17,.67,.83,.67); + const x = easeOut(0.5); // returns 0.627... + */ + // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. + const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * + t; + const subdivisionPrecision = 0.0000001; + const subdivisionMaxIterations = 12; + function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) { + let currentX; + let currentT; + let i = 0; + do { + currentT = lowerBound + (upperBound - lowerBound) / 2.0; + currentX = calcBezier(currentT, mX1, mX2) - x; + if (currentX > 0.0) { + upperBound = currentT; + } + else { + lowerBound = currentT; + } + } while (Math.abs(currentX) > subdivisionPrecision && + ++i < subdivisionMaxIterations); + return currentT; + } + function cubicBezier(mX1, mY1, mX2, mY2) { + // If this is a linear gradient, return linear easing + if (mX1 === mY1 && mX2 === mY2) + return noop; + const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2); + // If animation is at start/end, return t without easing + return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2); + } + + // Accepts an easing function and returns a new one that outputs mirrored values for + // the second half of the animation. Turns easeIn into easeInOut. + const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2; + + // Accepts an easing function and returns a new one that outputs reversed values. + // Turns easeIn into easeOut. + const reverseEasing = (easing) => (p) => 1 - easing(1 - p); + + const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99); + const backIn = /*@__PURE__*/ reverseEasing(backOut); + const backInOut = /*@__PURE__*/ mirrorEasing(backIn); + + const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); + + const circIn = (p) => 1 - Math.sin(Math.acos(p)); + const circOut = reverseEasing(circIn); + const circInOut = mirrorEasing(circIn); + + const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1); + const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1); + const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1); + + function steps(numSteps, direction = "end") { + return (progress) => { + progress = + direction === "end" + ? Math.min(progress, 0.999) + : Math.max(progress, 0.001); + const expanded = progress * numSteps; + const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded); + return clamp(0, 1, rounded / numSteps); + }; + } + + const isEasingArray = (ease) => { + return Array.isArray(ease) && typeof ease[0] !== "number"; + }; + + function getEasingForSegment(easing, i) { + return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing; + } + + const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number"; + + const easingLookup = { + linear: noop, + easeIn, + easeInOut, + easeOut, + circIn, + circInOut, + circOut, + backIn, + backInOut, + backOut, + anticipate, + }; + const isValidEasing = (easing) => { + return typeof easing === "string"; + }; + const easingDefinitionToFunction = (definition) => { + if (isBezierDefinition(definition)) { + // If cubic bezier definition, create bezier curve + exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`); + const [x1, y1, x2, y2] = definition; + return cubicBezier(x1, y1, x2, y2); + } + else if (isValidEasing(definition)) { + // Else lookup from table + exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`); + return easingLookup[definition]; + } + return definition; + }; + + exports.MotionGlobalConfig = MotionGlobalConfig; + exports.SubscriptionManager = SubscriptionManager; + exports.addUniqueItem = addUniqueItem; + exports.anticipate = anticipate; + exports.backIn = backIn; + exports.backInOut = backInOut; + exports.backOut = backOut; + exports.circIn = circIn; + exports.circInOut = circInOut; + exports.circOut = circOut; + exports.clamp = clamp; + exports.cubicBezier = cubicBezier; + exports.easeIn = easeIn; + exports.easeInOut = easeInOut; + exports.easeOut = easeOut; + exports.easingDefinitionToFunction = easingDefinitionToFunction; + exports.getEasingForSegment = getEasingForSegment; + exports.hasWarned = hasWarned; + exports.isBezierDefinition = isBezierDefinition; + exports.isEasingArray = isEasingArray; + exports.isNumericalString = isNumericalString; + exports.isObject = isObject; + exports.isZeroValueString = isZeroValueString; + exports.memo = memo; + exports.millisecondsToSeconds = millisecondsToSeconds; + exports.mirrorEasing = mirrorEasing; + exports.moveItem = moveItem; + exports.noop = noop; + exports.pipe = pipe; + exports.progress = progress; + exports.removeItem = removeItem; + exports.reverseEasing = reverseEasing; + exports.secondsToMilliseconds = secondsToMilliseconds; + exports.steps = steps; + exports.velocityPerSecond = velocityPerSecond; + exports.warnOnce = warnOnce; + exports.wrap = wrap; + +})); diff --git a/node_modules/motion-utils/dist/motion-utils.js b/node_modules/motion-utils/dist/motion-utils.js new file mode 100644 index 00000000..63088ffb --- /dev/null +++ b/node_modules/motion-utils/dist/motion-utils.js @@ -0,0 +1 @@ +!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).MotionUtils={})}(this,function(n){"use strict";function t(n,t){-1===n.indexOf(t)&&n.push(t)}function e(n,t){const e=n.indexOf(t);e>-1&&n.splice(e,1)}const i=(n,t,e)=>e>t?t:e{};const o=n=>n,r=(n,t)=>e=>t(n(e));const c=new Set;const u=(n,t,e)=>{const i=t-n;return((e-n)%i+i)%i+n},a=(n,t,e)=>(((1-3*e+3*t)*n+(3*e-6*t))*n+3*t)*n;function l(n,t,e,i){if(n===t&&e===i)return o;const s=t=>function(n,t,e,i,s){let o,r,c=0;do{r=t+(e-t)/2,o=a(r,i,s)-n,o>0?e=r:t=r}while(Math.abs(o)>1e-7&&++c<12);return r}(t,0,1,n,e);return n=>0===n||1===n?n:a(s(n),t,i)}const f=n=>t=>t<=.5?n(2*t)/2:(2-n(2*(1-t)))/2,p=n=>t=>1-n(1-t),d=l(.33,1.53,.69,.99),h=p(d),b=f(h),g=n=>(n*=2)<1?.5*h(n):.5*(2-Math.pow(2,-10*(n-1))),m=n=>1-Math.sin(Math.acos(n)),O=p(m),y=f(m),I=l(.42,0,1,1),M=l(0,0,.58,1),w=l(.42,0,.58,1);const S=n=>Array.isArray(n)&&"number"!=typeof n[0];const k=n=>Array.isArray(n)&&"number"==typeof n[0],v={linear:o,easeIn:I,easeInOut:w,easeOut:M,circIn:m,circInOut:y,circOut:O,backIn:h,backInOut:b,backOut:d,anticipate:g};n.MotionGlobalConfig={},n.SubscriptionManager=class{constructor(){this.subscriptions=[]}add(n){return t(this.subscriptions,n),()=>e(this.subscriptions,n)}notify(n,t,e){const i=this.subscriptions.length;if(i)if(1===i)this.subscriptions[0](n,t,e);else for(let s=0;s{if(k(n)){n.length;const[t,e,i,s]=n;return l(t,e,i,s)}return"string"==typeof n?v[n]:n},n.getEasingForSegment=function(n,t){return S(n)?n[u(0,n.length,t)]:n},n.hasWarned=function(n){return c.has(n)},n.invariant=s,n.isBezierDefinition=k,n.isEasingArray=S,n.isNumericalString=n=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(n),n.isObject=function(n){return"object"==typeof n&&null!==n},n.isZeroValueString=n=>/^0[^.\s]+$/u.test(n),n.memo=function(n){let t;return()=>(void 0===t&&(t=n()),t)},n.millisecondsToSeconds=n=>n/1e3,n.mirrorEasing=f,n.moveItem=function([...n],t,e){const i=t<0?n.length+t:t;if(i>=0&&in.reduce(r),n.progress=(n,t,e)=>{const i=t-n;return 0===i?1:(e-n)/i},n.removeItem=e,n.reverseEasing=p,n.secondsToMilliseconds=n=>1e3*n,n.steps=function(n,t="end"){return e=>{const s=(e="end"===t?Math.min(e,.999):Math.max(e,.001))*n,o="end"===t?Math.floor(s):Math.ceil(s);return i(0,1,o/n)}},n.velocityPerSecond=function(n,t){return t?n*(1e3/t):0},n.warnOnce=function(n,t,e){n||c.has(t)||(console.warn(t),e&&console.warn(e),c.add(t))},n.warning=()=>{},n.wrap=u}); diff --git a/node_modules/motion-utils/package.json b/node_modules/motion-utils/package.json new file mode 100644 index 00000000..da225e3e --- /dev/null +++ b/node_modules/motion-utils/package.json @@ -0,0 +1,26 @@ +{ + "name": "motion-utils", + "version": "12.19.0", + "author": "Matt Perry", + "license": "MIT", + "repository": "https://github.com/motiondivision/motion", + "main": "./dist/cjs/index.js", + "types": "./dist/index.d.ts", + "module": "./dist/es/index.mjs", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/cjs/index.js", + "import": "./dist/es/index.mjs", + "default": "./dist/cjs/index.js" + } + }, + "scripts": { + "clean": "rm -rf types dist lib", + "build": "yarn clean && tsc -p . && rollup -c", + "dev": "concurrently -c blue,red -n tsc,rollup --kill-others \"tsc --watch -p . --preserveWatchOutput\" \"rollup --config --watch --no-watch.clearScreen\"", + "test": "jest --config jest.config.json --max-workers=2" + }, + "gitHead": "009e4c4e793ecacaebe7066b969c640f0c09f573" +} diff --git a/node_modules/tslib/CopyrightNotice.txt b/node_modules/tslib/CopyrightNotice.txt new file mode 100644 index 00000000..5d7d2d9c --- /dev/null +++ b/node_modules/tslib/CopyrightNotice.txt @@ -0,0 +1,15 @@ +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ + diff --git a/node_modules/tslib/LICENSE.txt b/node_modules/tslib/LICENSE.txt new file mode 100644 index 00000000..fa7d1bdf --- /dev/null +++ b/node_modules/tslib/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/node_modules/tslib/README.md b/node_modules/tslib/README.md new file mode 100644 index 00000000..14b9a83d --- /dev/null +++ b/node_modules/tslib/README.md @@ -0,0 +1,164 @@ +# tslib + +This is a runtime library for [TypeScript](https://www.typescriptlang.org/) that contains all of the TypeScript helper functions. + +This library is primarily used by the `--importHelpers` flag in TypeScript. +When using `--importHelpers`, a module that uses helper functions like `__extends` and `__assign` in the following emitted file: + +```ts +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +exports.x = {}; +exports.y = __assign({}, exports.x); + +``` + +will instead be emitted as something like the following: + +```ts +var tslib_1 = require("tslib"); +exports.x = {}; +exports.y = tslib_1.__assign({}, exports.x); +``` + +Because this can avoid duplicate declarations of things like `__extends`, `__assign`, etc., this means delivering users smaller files on average, as well as less runtime overhead. +For optimized bundles with TypeScript, you should absolutely consider using `tslib` and `--importHelpers`. + +# Installing + +For the latest stable version, run: + +## npm + +```sh +# TypeScript 3.9.2 or later +npm install tslib + +# TypeScript 3.8.4 or earlier +npm install tslib@^1 + +# TypeScript 2.3.2 or earlier +npm install tslib@1.6.1 +``` + +## yarn + +```sh +# TypeScript 3.9.2 or later +yarn add tslib + +# TypeScript 3.8.4 or earlier +yarn add tslib@^1 + +# TypeScript 2.3.2 or earlier +yarn add tslib@1.6.1 +``` + +## bower + +```sh +# TypeScript 3.9.2 or later +bower install tslib + +# TypeScript 3.8.4 or earlier +bower install tslib@^1 + +# TypeScript 2.3.2 or earlier +bower install tslib@1.6.1 +``` + +## JSPM + +```sh +# TypeScript 3.9.2 or later +jspm install tslib + +# TypeScript 3.8.4 or earlier +jspm install tslib@^1 + +# TypeScript 2.3.2 or earlier +jspm install tslib@1.6.1 +``` + +# Usage + +Set the `importHelpers` compiler option on the command line: + +``` +tsc --importHelpers file.ts +``` + +or in your tsconfig.json: + +```json +{ + "compilerOptions": { + "importHelpers": true + } +} +``` + +#### For bower and JSPM users + +You will need to add a `paths` mapping for `tslib`, e.g. For Bower users: + +```json +{ + "compilerOptions": { + "module": "amd", + "importHelpers": true, + "baseUrl": "./", + "paths": { + "tslib" : ["bower_components/tslib/tslib.d.ts"] + } + } +} +``` + +For JSPM users: + +```json +{ + "compilerOptions": { + "module": "system", + "importHelpers": true, + "baseUrl": "./", + "paths": { + "tslib" : ["jspm_packages/npm/tslib@2.x.y/tslib.d.ts"] + } + } +} +``` + +## Deployment + +- Choose your new version number +- Set it in `package.json` and `bower.json` +- Create a tag: `git tag [version]` +- Push the tag: `git push --tags` +- Create a [release in GitHub](https://github.com/microsoft/tslib/releases) +- Run the [publish to npm](https://github.com/microsoft/tslib/actions?query=workflow%3A%22Publish+to+NPM%22) workflow + +Done. + +# Contribute + +There are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md) to TypeScript. + +* [Submit bugs](https://github.com/Microsoft/TypeScript/issues) and help us verify fixes as they are checked in. +* Review the [source code changes](https://github.com/Microsoft/TypeScript/pulls). +* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript). +* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter. +* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md). + +# Documentation + +* [Quick tutorial](http://www.typescriptlang.org/Tutorial) +* [Programming handbook](http://www.typescriptlang.org/Handbook) +* [Homepage](http://www.typescriptlang.org/) diff --git a/node_modules/tslib/SECURITY.md b/node_modules/tslib/SECURITY.md new file mode 100644 index 00000000..869fdfe2 --- /dev/null +++ b/node_modules/tslib/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/node_modules/tslib/modules/index.d.ts b/node_modules/tslib/modules/index.d.ts new file mode 100644 index 00000000..3244fabe --- /dev/null +++ b/node_modules/tslib/modules/index.d.ts @@ -0,0 +1,38 @@ +// Note: named reexports are used instead of `export *` because +// TypeScript itself doesn't resolve the `export *` when checking +// if a particular helper exists. +export { + __extends, + __assign, + __rest, + __decorate, + __param, + __esDecorate, + __runInitializers, + __propKey, + __setFunctionName, + __metadata, + __awaiter, + __generator, + __exportStar, + __values, + __read, + __spread, + __spreadArrays, + __spreadArray, + __await, + __asyncGenerator, + __asyncDelegator, + __asyncValues, + __makeTemplateObject, + __importStar, + __importDefault, + __classPrivateFieldGet, + __classPrivateFieldSet, + __classPrivateFieldIn, + __createBinding, + __addDisposableResource, + __disposeResources, + __rewriteRelativeImportExtension, +} from '../tslib.js'; +export * as default from '../tslib.js'; diff --git a/node_modules/tslib/modules/index.js b/node_modules/tslib/modules/index.js new file mode 100644 index 00000000..f4f9a06d --- /dev/null +++ b/node_modules/tslib/modules/index.js @@ -0,0 +1,70 @@ +import tslib from '../tslib.js'; +const { + __extends, + __assign, + __rest, + __decorate, + __param, + __esDecorate, + __runInitializers, + __propKey, + __setFunctionName, + __metadata, + __awaiter, + __generator, + __exportStar, + __createBinding, + __values, + __read, + __spread, + __spreadArrays, + __spreadArray, + __await, + __asyncGenerator, + __asyncDelegator, + __asyncValues, + __makeTemplateObject, + __importStar, + __importDefault, + __classPrivateFieldGet, + __classPrivateFieldSet, + __classPrivateFieldIn, + __addDisposableResource, + __disposeResources, + __rewriteRelativeImportExtension, +} = tslib; +export { + __extends, + __assign, + __rest, + __decorate, + __param, + __esDecorate, + __runInitializers, + __propKey, + __setFunctionName, + __metadata, + __awaiter, + __generator, + __exportStar, + __createBinding, + __values, + __read, + __spread, + __spreadArrays, + __spreadArray, + __await, + __asyncGenerator, + __asyncDelegator, + __asyncValues, + __makeTemplateObject, + __importStar, + __importDefault, + __classPrivateFieldGet, + __classPrivateFieldSet, + __classPrivateFieldIn, + __addDisposableResource, + __disposeResources, + __rewriteRelativeImportExtension, +}; +export default tslib; diff --git a/node_modules/tslib/modules/package.json b/node_modules/tslib/modules/package.json new file mode 100644 index 00000000..96ae6e57 --- /dev/null +++ b/node_modules/tslib/modules/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} \ No newline at end of file diff --git a/node_modules/tslib/package.json b/node_modules/tslib/package.json new file mode 100644 index 00000000..57d05787 --- /dev/null +++ b/node_modules/tslib/package.json @@ -0,0 +1,47 @@ +{ + "name": "tslib", + "author": "Microsoft Corp.", + "homepage": "https://www.typescriptlang.org/", + "version": "2.8.1", + "license": "0BSD", + "description": "Runtime library for TypeScript helper functions", + "keywords": [ + "TypeScript", + "Microsoft", + "compiler", + "language", + "javascript", + "tslib", + "runtime" + ], + "bugs": { + "url": "https://github.com/Microsoft/TypeScript/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/tslib.git" + }, + "main": "tslib.js", + "module": "tslib.es6.js", + "jsnext:main": "tslib.es6.js", + "typings": "tslib.d.ts", + "sideEffects": false, + "exports": { + ".": { + "module": { + "types": "./modules/index.d.ts", + "default": "./tslib.es6.mjs" + }, + "import": { + "node": "./modules/index.js", + "default": { + "types": "./modules/index.d.ts", + "default": "./tslib.es6.mjs" + } + }, + "default": "./tslib.js" + }, + "./*": "./*", + "./": "./" + } +} diff --git a/node_modules/tslib/tslib.d.ts b/node_modules/tslib/tslib.d.ts new file mode 100644 index 00000000..9c27e058 --- /dev/null +++ b/node_modules/tslib/tslib.d.ts @@ -0,0 +1,460 @@ +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ + +/** + * Used to shim class extends. + * + * @param d The derived class. + * @param b The base class. + */ +export declare function __extends(d: Function, b: Function): void; + +/** + * Copy the values of all of the enumerable own properties from one or more source objects to a + * target object. Returns the target object. + * + * @param t The target object to copy to. + * @param sources One or more source objects from which to copy properties + */ +export declare function __assign(t: any, ...sources: any[]): any; + +/** + * Performs a rest spread on an object. + * + * @param t The source value. + * @param propertyNames The property names excluded from the rest spread. + */ +export declare function __rest(t: any, propertyNames: (string | symbol)[]): any; + +/** + * Applies decorators to a target object + * + * @param decorators The set of decorators to apply. + * @param target The target object. + * @param key If specified, the own property to apply the decorators to. + * @param desc The property descriptor, defaults to fetching the descriptor from the target object. + * @experimental + */ +export declare function __decorate(decorators: Function[], target: any, key?: string | symbol, desc?: any): any; + +/** + * Creates an observing function decorator from a parameter decorator. + * + * @param paramIndex The parameter index to apply the decorator to. + * @param decorator The parameter decorator to apply. Note that the return value is ignored. + * @experimental + */ +export declare function __param(paramIndex: number, decorator: Function): Function; + +/** + * Applies decorators to a class or class member, following the native ECMAScript decorator specification. + * @param ctor For non-field class members, the class constructor. Otherwise, `null`. + * @param descriptorIn The `PropertyDescriptor` to use when unable to look up the property from `ctor`. + * @param decorators The decorators to apply + * @param contextIn The `DecoratorContext` to clone for each decorator application. + * @param initializers An array of field initializer mutation functions into which new initializers are written. + * @param extraInitializers An array of extra initializer functions into which new initializers are written. + */ +export declare function __esDecorate(ctor: Function | null, descriptorIn: object | null, decorators: Function[], contextIn: object, initializers: Function[] | null, extraInitializers: Function[]): void; + +/** + * Runs field initializers or extra initializers generated by `__esDecorate`. + * @param thisArg The `this` argument to use. + * @param initializers The array of initializers to evaluate. + * @param value The initial value to pass to the initializers. + */ +export declare function __runInitializers(thisArg: unknown, initializers: Function[], value?: any): any; + +/** + * Converts a computed property name into a `string` or `symbol` value. + */ +export declare function __propKey(x: any): string | symbol; + +/** + * Assigns the name of a function derived from the left-hand side of an assignment. + * @param f The function to rename. + * @param name The new name for the function. + * @param prefix A prefix (such as `"get"` or `"set"`) to insert before the name. + */ +export declare function __setFunctionName(f: Function, name: string | symbol, prefix?: string): Function; + +/** + * Creates a decorator that sets metadata. + * + * @param metadataKey The metadata key + * @param metadataValue The metadata value + * @experimental + */ +export declare function __metadata(metadataKey: any, metadataValue: any): Function; + +/** + * Converts a generator function into a pseudo-async function, by treating each `yield` as an `await`. + * + * @param thisArg The reference to use as the `this` value in the generator function + * @param _arguments The optional arguments array + * @param P The optional promise constructor argument, defaults to the `Promise` property of the global object. + * @param generator The generator function + */ +export declare function __awaiter(thisArg: any, _arguments: any, P: Function, generator: Function): any; + +/** + * Creates an Iterator object using the body as the implementation. + * + * @param thisArg The reference to use as the `this` value in the function + * @param body The generator state-machine based implementation. + * + * @see [./docs/generator.md] + */ +export declare function __generator(thisArg: any, body: Function): any; + +/** + * Creates bindings for all enumerable properties of `m` on `exports` + * + * @param m The source object + * @param o The `exports` object. + */ +export declare function __exportStar(m: any, o: any): void; + +/** + * Creates a value iterator from an `Iterable` or `ArrayLike` object. + * + * @param o The object. + * @throws {TypeError} If `o` is neither `Iterable`, nor an `ArrayLike`. + */ +export declare function __values(o: any): any; + +/** + * Reads values from an `Iterable` or `ArrayLike` object and returns the resulting array. + * + * @param o The object to read from. + * @param n The maximum number of arguments to read, defaults to `Infinity`. + */ +export declare function __read(o: any, n?: number): any[]; + +/** + * Creates an array from iterable spread. + * + * @param args The Iterable objects to spread. + * @deprecated since TypeScript 4.2 - Use `__spreadArray` + */ +export declare function __spread(...args: any[][]): any[]; + +/** + * Creates an array from array spread. + * + * @param args The ArrayLikes to spread into the resulting array. + * @deprecated since TypeScript 4.2 - Use `__spreadArray` + */ +export declare function __spreadArrays(...args: any[][]): any[]; + +/** + * Spreads the `from` array into the `to` array. + * + * @param pack Replace empty elements with `undefined`. + */ +export declare function __spreadArray(to: any[], from: any[], pack?: boolean): any[]; + +/** + * Creates an object that signals to `__asyncGenerator` that it shouldn't be yielded, + * and instead should be awaited and the resulting value passed back to the generator. + * + * @param v The value to await. + */ +export declare function __await(v: any): any; + +/** + * Converts a generator function into an async generator function, by using `yield __await` + * in place of normal `await`. + * + * @param thisArg The reference to use as the `this` value in the generator function + * @param _arguments The optional arguments array + * @param generator The generator function + */ +export declare function __asyncGenerator(thisArg: any, _arguments: any, generator: Function): any; + +/** + * Used to wrap a potentially async iterator in such a way so that it wraps the result + * of calling iterator methods of `o` in `__await` instances, and then yields the awaited values. + * + * @param o The potentially async iterator. + * @returns A synchronous iterator yielding `__await` instances on every odd invocation + * and returning the awaited `IteratorResult` passed to `next` every even invocation. + */ +export declare function __asyncDelegator(o: any): any; + +/** + * Creates a value async iterator from an `AsyncIterable`, `Iterable` or `ArrayLike` object. + * + * @param o The object. + * @throws {TypeError} If `o` is neither `AsyncIterable`, `Iterable`, nor an `ArrayLike`. + */ +export declare function __asyncValues(o: any): any; + +/** + * Creates a `TemplateStringsArray` frozen object from the `cooked` and `raw` arrays. + * + * @param cooked The cooked possibly-sparse array. + * @param raw The raw string content. + */ +export declare function __makeTemplateObject(cooked: string[], raw: string[]): TemplateStringsArray; + +/** + * Used to shim default and named imports in ECMAScript Modules transpiled to CommonJS. + * + * ```js + * import Default, { Named, Other } from "mod"; + * // or + * import { default as Default, Named, Other } from "mod"; + * ``` + * + * @param mod The CommonJS module exports object. + */ +export declare function __importStar(mod: T): T; + +/** + * Used to shim default imports in ECMAScript Modules transpiled to CommonJS. + * + * ```js + * import Default from "mod"; + * ``` + * + * @param mod The CommonJS module exports object. + */ +export declare function __importDefault(mod: T): T | { default: T }; + +/** + * Emulates reading a private instance field. + * + * @param receiver The instance from which to read the private field. + * @param state A WeakMap containing the private field value for an instance. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * + * @throws {TypeError} If `state` doesn't have an entry for `receiver`. + */ +export declare function __classPrivateFieldGet( + receiver: T, + state: { has(o: T): boolean, get(o: T): V | undefined }, + kind?: "f" +): V; + +/** + * Emulates reading a private static field. + * + * @param receiver The object from which to read the private static field. + * @param state The class constructor containing the definition of the static field. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The descriptor that holds the static field value. + * + * @throws {TypeError} If `receiver` is not `state`. + */ +export declare function __classPrivateFieldGet unknown, V>( + receiver: T, + state: T, + kind: "f", + f: { value: V } +): V; + +/** + * Emulates evaluating a private instance "get" accessor. + * + * @param receiver The instance on which to evaluate the private "get" accessor. + * @param state A WeakSet used to verify an instance supports the private "get" accessor. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The "get" accessor function to evaluate. + * + * @throws {TypeError} If `state` doesn't have an entry for `receiver`. + */ +export declare function __classPrivateFieldGet( + receiver: T, + state: { has(o: T): boolean }, + kind: "a", + f: () => V +): V; + +/** + * Emulates evaluating a private static "get" accessor. + * + * @param receiver The object on which to evaluate the private static "get" accessor. + * @param state The class constructor containing the definition of the static "get" accessor. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The "get" accessor function to evaluate. + * + * @throws {TypeError} If `receiver` is not `state`. + */ +export declare function __classPrivateFieldGet unknown, V>( + receiver: T, + state: T, + kind: "a", + f: () => V +): V; + +/** + * Emulates reading a private instance method. + * + * @param receiver The instance from which to read a private method. + * @param state A WeakSet used to verify an instance supports the private method. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The function to return as the private instance method. + * + * @throws {TypeError} If `state` doesn't have an entry for `receiver`. + */ +export declare function __classPrivateFieldGet unknown>( + receiver: T, + state: { has(o: T): boolean }, + kind: "m", + f: V +): V; + +/** + * Emulates reading a private static method. + * + * @param receiver The object from which to read the private static method. + * @param state The class constructor containing the definition of the static method. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The function to return as the private static method. + * + * @throws {TypeError} If `receiver` is not `state`. + */ +export declare function __classPrivateFieldGet unknown, V extends (...args: any[]) => unknown>( + receiver: T, + state: T, + kind: "m", + f: V +): V; + +/** + * Emulates writing to a private instance field. + * + * @param receiver The instance on which to set a private field value. + * @param state A WeakMap used to store the private field value for an instance. + * @param value The value to store in the private field. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * + * @throws {TypeError} If `state` doesn't have an entry for `receiver`. + */ +export declare function __classPrivateFieldSet( + receiver: T, + state: { has(o: T): boolean, set(o: T, value: V): unknown }, + value: V, + kind?: "f" +): V; + +/** + * Emulates writing to a private static field. + * + * @param receiver The object on which to set the private static field. + * @param state The class constructor containing the definition of the private static field. + * @param value The value to store in the private field. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The descriptor that holds the static field value. + * + * @throws {TypeError} If `receiver` is not `state`. + */ +export declare function __classPrivateFieldSet unknown, V>( + receiver: T, + state: T, + value: V, + kind: "f", + f: { value: V } +): V; + +/** + * Emulates writing to a private instance "set" accessor. + * + * @param receiver The instance on which to evaluate the private instance "set" accessor. + * @param state A WeakSet used to verify an instance supports the private "set" accessor. + * @param value The value to store in the private accessor. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The "set" accessor function to evaluate. + * + * @throws {TypeError} If `state` doesn't have an entry for `receiver`. + */ +export declare function __classPrivateFieldSet( + receiver: T, + state: { has(o: T): boolean }, + value: V, + kind: "a", + f: (v: V) => void +): V; + +/** + * Emulates writing to a private static "set" accessor. + * + * @param receiver The object on which to evaluate the private static "set" accessor. + * @param state The class constructor containing the definition of the static "set" accessor. + * @param value The value to store in the private field. + * @param kind Either `"f"` for a field, `"a"` for an accessor, or `"m"` for a method. + * @param f The "set" accessor function to evaluate. + * + * @throws {TypeError} If `receiver` is not `state`. + */ +export declare function __classPrivateFieldSet unknown, V>( + receiver: T, + state: T, + value: V, + kind: "a", + f: (v: V) => void +): V; + +/** + * Checks for the existence of a private field/method/accessor. + * + * @param state The class constructor containing the static member, or the WeakMap or WeakSet associated with a private instance member. + * @param receiver The object for which to test the presence of the private member. + */ +export declare function __classPrivateFieldIn( + state: (new (...args: any[]) => unknown) | { has(o: any): boolean }, + receiver: unknown, +): boolean; + +/** + * Creates a re-export binding on `object` with key `objectKey` that references `target[key]`. + * + * @param object The local `exports` object. + * @param target The object to re-export from. + * @param key The property key of `target` to re-export. + * @param objectKey The property key to re-export as. Defaults to `key`. + */ +export declare function __createBinding(object: object, target: object, key: PropertyKey, objectKey?: PropertyKey): void; + +/** + * Adds a disposable resource to a resource-tracking environment object. + * @param env A resource-tracking environment object. + * @param value Either a Disposable or AsyncDisposable object, `null`, or `undefined`. + * @param async When `true`, `AsyncDisposable` resources can be added. When `false`, `AsyncDisposable` resources cannot be added. + * @returns The {@link value} argument. + * + * @throws {TypeError} If {@link value} is not an object, or if either `Symbol.dispose` or `Symbol.asyncDispose` are not + * defined, or if {@link value} does not have an appropriate `Symbol.dispose` or `Symbol.asyncDispose` method. + */ +export declare function __addDisposableResource(env: { stack: { value?: unknown, dispose?: Function, async: boolean }[]; error: unknown; hasError: boolean; }, value: T, async: boolean): T; + +/** + * Disposes all resources in a resource-tracking environment object. + * @param env A resource-tracking environment object. + * @returns A {@link Promise} if any resources in the environment were marked as `async` when added; otherwise, `void`. + * + * @throws {SuppressedError} if an error thrown during disposal would have suppressed a prior error from disposal or the + * error recorded in the resource-tracking environment object. + * @seealso {@link __addDisposableResource} + */ +export declare function __disposeResources(env: { stack: { value?: unknown, dispose?: Function, async: boolean }[]; error: unknown; hasError: boolean; }): any; + +/** + * Transforms a relative import specifier ending in a non-declaration TypeScript file extension to its JavaScript file extension counterpart. + * @param path The import specifier. + * @param preserveJsx Causes '*.tsx' to transform to '*.jsx' instead of '*.js'. Should be true when `--jsx` is set to `preserve`. + */ +export declare function __rewriteRelativeImportExtension(path: string, preserveJsx?: boolean): string; \ No newline at end of file diff --git a/node_modules/tslib/tslib.es6.html b/node_modules/tslib/tslib.es6.html new file mode 100644 index 00000000..b122e41b --- /dev/null +++ b/node_modules/tslib/tslib.es6.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/node_modules/tslib/tslib.es6.js b/node_modules/tslib/tslib.es6.js new file mode 100644 index 00000000..67ba5c5a --- /dev/null +++ b/node_modules/tslib/tslib.es6.js @@ -0,0 +1,402 @@ +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +export function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +export var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + } + return __assign.apply(this, arguments); +} + +export function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +} + +export function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +export function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +} + +export function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; + +export function __runInitializers(thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; + +export function __propKey(x) { + return typeof x === "symbol" ? x : "".concat(x); +}; + +export function __setFunctionName(f, name, prefix) { + if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; + return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); +}; + +export function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); +} + +export function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +export function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +export var __createBinding = Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +}); + +export function __exportStar(m, o) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); +} + +export function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +} + +export function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +} + +/** @deprecated */ +export function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; +} + +/** @deprecated */ +export function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +} + +export function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +} + +export function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); +} + +export function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; + function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } + function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +} + +export function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; } +} + +export function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +} + +export function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; + +var __setModuleDefault = Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}; + +var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); +}; + +export function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; +} + +export function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; +} + +export function __classPrivateFieldGet(receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +} + +export function __classPrivateFieldSet(receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +} + +export function __classPrivateFieldIn(state, receiver) { + if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); + return typeof state === "function" ? receiver === state : state.has(receiver); +} + +export function __addDisposableResource(env, value, async) { + if (value !== null && value !== void 0) { + if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); + var dispose, inner; + if (async) { + if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); + dispose = value[Symbol.asyncDispose]; + } + if (dispose === void 0) { + if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); + dispose = value[Symbol.dispose]; + if (async) inner = dispose; + } + if (typeof dispose !== "function") throw new TypeError("Object not disposable."); + if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; + env.stack.push({ value: value, dispose: dispose, async: async }); + } + else if (async) { + env.stack.push({ async: true }); + } + return value; + +} + +var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +export function __disposeResources(env) { + function fail(e) { + env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; + env.hasError = true; + } + var r, s = 0; + function next() { + while (r = env.stack.pop()) { + try { + if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); + if (r.dispose) { + var result = r.dispose.call(r.value); + if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); + } + else s |= 1; + } + catch (e) { + fail(e); + } + } + if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); + if (env.hasError) throw env.error; + } + return next(); +} + +export function __rewriteRelativeImportExtension(path, preserveJsx) { + if (typeof path === "string" && /^\.\.?\//.test(path)) { + return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { + return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); + }); + } + return path; +} + +export default { + __extends: __extends, + __assign: __assign, + __rest: __rest, + __decorate: __decorate, + __param: __param, + __esDecorate: __esDecorate, + __runInitializers: __runInitializers, + __propKey: __propKey, + __setFunctionName: __setFunctionName, + __metadata: __metadata, + __awaiter: __awaiter, + __generator: __generator, + __createBinding: __createBinding, + __exportStar: __exportStar, + __values: __values, + __read: __read, + __spread: __spread, + __spreadArrays: __spreadArrays, + __spreadArray: __spreadArray, + __await: __await, + __asyncGenerator: __asyncGenerator, + __asyncDelegator: __asyncDelegator, + __asyncValues: __asyncValues, + __makeTemplateObject: __makeTemplateObject, + __importStar: __importStar, + __importDefault: __importDefault, + __classPrivateFieldGet: __classPrivateFieldGet, + __classPrivateFieldSet: __classPrivateFieldSet, + __classPrivateFieldIn: __classPrivateFieldIn, + __addDisposableResource: __addDisposableResource, + __disposeResources: __disposeResources, + __rewriteRelativeImportExtension: __rewriteRelativeImportExtension, +}; diff --git a/node_modules/tslib/tslib.es6.mjs b/node_modules/tslib/tslib.es6.mjs new file mode 100644 index 00000000..c17990a1 --- /dev/null +++ b/node_modules/tslib/tslib.es6.mjs @@ -0,0 +1,401 @@ +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +export function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +export var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + } + return __assign.apply(this, arguments); +} + +export function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +} + +export function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +export function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +} + +export function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; + +export function __runInitializers(thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; + +export function __propKey(x) { + return typeof x === "symbol" ? x : "".concat(x); +}; + +export function __setFunctionName(f, name, prefix) { + if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; + return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); +}; + +export function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); +} + +export function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +export function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +export var __createBinding = Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +}); + +export function __exportStar(m, o) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); +} + +export function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +} + +export function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +} + +/** @deprecated */ +export function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; +} + +/** @deprecated */ +export function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +} + +export function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +} + +export function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); +} + +export function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; + function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } + function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +} + +export function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; } +} + +export function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +} + +export function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; + +var __setModuleDefault = Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}; + +var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); +}; + +export function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; +} + +export function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; +} + +export function __classPrivateFieldGet(receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +} + +export function __classPrivateFieldSet(receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +} + +export function __classPrivateFieldIn(state, receiver) { + if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); + return typeof state === "function" ? receiver === state : state.has(receiver); +} + +export function __addDisposableResource(env, value, async) { + if (value !== null && value !== void 0) { + if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); + var dispose, inner; + if (async) { + if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); + dispose = value[Symbol.asyncDispose]; + } + if (dispose === void 0) { + if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); + dispose = value[Symbol.dispose]; + if (async) inner = dispose; + } + if (typeof dispose !== "function") throw new TypeError("Object not disposable."); + if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; + env.stack.push({ value: value, dispose: dispose, async: async }); + } + else if (async) { + env.stack.push({ async: true }); + } + return value; +} + +var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +export function __disposeResources(env) { + function fail(e) { + env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; + env.hasError = true; + } + var r, s = 0; + function next() { + while (r = env.stack.pop()) { + try { + if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); + if (r.dispose) { + var result = r.dispose.call(r.value); + if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); + } + else s |= 1; + } + catch (e) { + fail(e); + } + } + if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); + if (env.hasError) throw env.error; + } + return next(); +} + +export function __rewriteRelativeImportExtension(path, preserveJsx) { + if (typeof path === "string" && /^\.\.?\//.test(path)) { + return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { + return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); + }); + } + return path; +} + +export default { + __extends, + __assign, + __rest, + __decorate, + __param, + __esDecorate, + __runInitializers, + __propKey, + __setFunctionName, + __metadata, + __awaiter, + __generator, + __createBinding, + __exportStar, + __values, + __read, + __spread, + __spreadArrays, + __spreadArray, + __await, + __asyncGenerator, + __asyncDelegator, + __asyncValues, + __makeTemplateObject, + __importStar, + __importDefault, + __classPrivateFieldGet, + __classPrivateFieldSet, + __classPrivateFieldIn, + __addDisposableResource, + __disposeResources, + __rewriteRelativeImportExtension, +}; diff --git a/node_modules/tslib/tslib.html b/node_modules/tslib/tslib.html new file mode 100644 index 00000000..44c9ba51 --- /dev/null +++ b/node_modules/tslib/tslib.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/node_modules/tslib/tslib.js b/node_modules/tslib/tslib.js new file mode 100644 index 00000000..b6509f77 --- /dev/null +++ b/node_modules/tslib/tslib.js @@ -0,0 +1,484 @@ +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global global, define, Symbol, Reflect, Promise, SuppressedError, Iterator */ +var __extends; +var __assign; +var __rest; +var __decorate; +var __param; +var __esDecorate; +var __runInitializers; +var __propKey; +var __setFunctionName; +var __metadata; +var __awaiter; +var __generator; +var __exportStar; +var __values; +var __read; +var __spread; +var __spreadArrays; +var __spreadArray; +var __await; +var __asyncGenerator; +var __asyncDelegator; +var __asyncValues; +var __makeTemplateObject; +var __importStar; +var __importDefault; +var __classPrivateFieldGet; +var __classPrivateFieldSet; +var __classPrivateFieldIn; +var __createBinding; +var __addDisposableResource; +var __disposeResources; +var __rewriteRelativeImportExtension; +(function (factory) { + var root = typeof global === "object" ? global : typeof self === "object" ? self : typeof this === "object" ? this : {}; + if (typeof define === "function" && define.amd) { + define("tslib", ["exports"], function (exports) { factory(createExporter(root, createExporter(exports))); }); + } + else if (typeof module === "object" && typeof module.exports === "object") { + factory(createExporter(root, createExporter(module.exports))); + } + else { + factory(createExporter(root)); + } + function createExporter(exports, previous) { + if (exports !== root) { + if (typeof Object.create === "function") { + Object.defineProperty(exports, "__esModule", { value: true }); + } + else { + exports.__esModule = true; + } + } + return function (id, v) { return exports[id] = previous ? previous(id, v) : v; }; + } +}) +(function (exporter) { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + + __extends = function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + + __assign = Object.assign || function (t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + + __rest = function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; + }; + + __decorate = function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + + __param = function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } + }; + + __esDecorate = function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; + }; + + __runInitializers = function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; + }; + + __propKey = function (x) { + return typeof x === "symbol" ? x : "".concat(x); + }; + + __setFunctionName = function (f, name, prefix) { + if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; + return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); + }; + + __metadata = function (metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); + }; + + __awaiter = function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; + + __generator = function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + }; + + __exportStar = function(m, o) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); + }; + + __createBinding = Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }); + + __values = function (o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + }; + + __read = function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; + }; + + /** @deprecated */ + __spread = function () { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; + }; + + /** @deprecated */ + __spreadArrays = function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; + }; + + __spreadArray = function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); + }; + + __await = function (v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); + }; + + __asyncGenerator = function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; + function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } + function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } + }; + + __asyncDelegator = function (o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; } + }; + + __asyncValues = function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } + }; + + __makeTemplateObject = function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; + }; + + var __setModuleDefault = Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }; + + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + + __importStar = function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; + + __importDefault = function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; + }; + + __classPrivateFieldGet = function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); + }; + + __classPrivateFieldSet = function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; + }; + + __classPrivateFieldIn = function (state, receiver) { + if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); + return typeof state === "function" ? receiver === state : state.has(receiver); + }; + + __addDisposableResource = function (env, value, async) { + if (value !== null && value !== void 0) { + if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); + var dispose, inner; + if (async) { + if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); + dispose = value[Symbol.asyncDispose]; + } + if (dispose === void 0) { + if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); + dispose = value[Symbol.dispose]; + if (async) inner = dispose; + } + if (typeof dispose !== "function") throw new TypeError("Object not disposable."); + if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; + env.stack.push({ value: value, dispose: dispose, async: async }); + } + else if (async) { + env.stack.push({ async: true }); + } + return value; + }; + + var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; + }; + + __disposeResources = function (env) { + function fail(e) { + env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; + env.hasError = true; + } + var r, s = 0; + function next() { + while (r = env.stack.pop()) { + try { + if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); + if (r.dispose) { + var result = r.dispose.call(r.value); + if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); + } + else s |= 1; + } + catch (e) { + fail(e); + } + } + if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); + if (env.hasError) throw env.error; + } + return next(); + }; + + __rewriteRelativeImportExtension = function (path, preserveJsx) { + if (typeof path === "string" && /^\.\.?\//.test(path)) { + return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { + return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); + }); + } + return path; + }; + + exporter("__extends", __extends); + exporter("__assign", __assign); + exporter("__rest", __rest); + exporter("__decorate", __decorate); + exporter("__param", __param); + exporter("__esDecorate", __esDecorate); + exporter("__runInitializers", __runInitializers); + exporter("__propKey", __propKey); + exporter("__setFunctionName", __setFunctionName); + exporter("__metadata", __metadata); + exporter("__awaiter", __awaiter); + exporter("__generator", __generator); + exporter("__exportStar", __exportStar); + exporter("__createBinding", __createBinding); + exporter("__values", __values); + exporter("__read", __read); + exporter("__spread", __spread); + exporter("__spreadArrays", __spreadArrays); + exporter("__spreadArray", __spreadArray); + exporter("__await", __await); + exporter("__asyncGenerator", __asyncGenerator); + exporter("__asyncDelegator", __asyncDelegator); + exporter("__asyncValues", __asyncValues); + exporter("__makeTemplateObject", __makeTemplateObject); + exporter("__importStar", __importStar); + exporter("__importDefault", __importDefault); + exporter("__classPrivateFieldGet", __classPrivateFieldGet); + exporter("__classPrivateFieldSet", __classPrivateFieldSet); + exporter("__classPrivateFieldIn", __classPrivateFieldIn); + exporter("__addDisposableResource", __addDisposableResource); + exporter("__disposeResources", __disposeResources); + exporter("__rewriteRelativeImportExtension", __rewriteRelativeImportExtension); +}); + +0 && (module.exports = { + __extends: __extends, + __assign: __assign, + __rest: __rest, + __decorate: __decorate, + __param: __param, + __esDecorate: __esDecorate, + __runInitializers: __runInitializers, + __propKey: __propKey, + __setFunctionName: __setFunctionName, + __metadata: __metadata, + __awaiter: __awaiter, + __generator: __generator, + __exportStar: __exportStar, + __createBinding: __createBinding, + __values: __values, + __read: __read, + __spread: __spread, + __spreadArrays: __spreadArrays, + __spreadArray: __spreadArray, + __await: __await, + __asyncGenerator: __asyncGenerator, + __asyncDelegator: __asyncDelegator, + __asyncValues: __asyncValues, + __makeTemplateObject: __makeTemplateObject, + __importStar: __importStar, + __importDefault: __importDefault, + __classPrivateFieldGet: __classPrivateFieldGet, + __classPrivateFieldSet: __classPrivateFieldSet, + __classPrivateFieldIn: __classPrivateFieldIn, + __addDisposableResource: __addDisposableResource, + __disposeResources: __disposeResources, + __rewriteRelativeImportExtension: __rewriteRelativeImportExtension, +}); diff --git a/package.json b/package.json new file mode 100644 index 00000000..e4c4d4b5 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "framer-motion": "^12.23.0" + } +}