diff --git a/bin/build_package.ts b/bin/build_package.ts index 08c079b56d2..c773838429a 100644 --- a/bin/build_package.ts +++ b/bin/build_package.ts @@ -5,10 +5,9 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import { parseArgs } from 'node:util'; -import * as LightningCSS from 'lightningcss'; -import * as rollup from 'rollup'; import { globSync } from 'tinyglobby'; -import { getRollupConfiguration } from './rollup.ts'; +import { build } from 'tsup'; +import { readPackageJSON } from "pkg-types"; const args = parseArgs({ allowPositionals: true, @@ -34,117 +33,105 @@ async function main() { process.exit(1); } - const packageData = await import(path.join(packageRoot, 'package.json'), {with: { type: 'json'}}); - const packageName = packageData.name; - const srcDir = path.join(packageRoot, 'src'); - const distDir = path.join(packageRoot, 'dist'); + const packageData = await readPackageJSON(path.join(packageRoot, 'package.json')); + const isStimulusBundle = '@symfony/stimulus-bundle' === packageData.name; + const isReactOrVueOrSvelte = ['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].some(name => packageData.name.startsWith(name)); - if (!fs.existsSync(srcDir)) { - console.error(`The package directory "${packageRoot}" does not contain a "src" directory.`); - process.exit(1); - } - - if (fs.existsSync(distDir)) { - console.log(`Cleaning up the "${distDir}" directory...`); - await fs.promises.rm(distDir, { recursive: true }); - await fs.promises.mkdir(distDir); - } - - const inputScriptFiles = [ - ...globSync(path.join(srcDir, '*controller.ts')), - ...(['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].includes(packageName) - ? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'components.ts')] - : []), - ...(packageName === '@symfony/stimulus-bundle' - ? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'controllers.ts')] - : []), + const inputCssFile = packageData?.config?.css_source; + const inputFiles = [ + ...globSync('src/*controller.ts'), + ...(isStimulusBundle ? ['src/loader.ts', 'src/controllers.ts'] : []), + ...(isReactOrVueOrSvelte ? ['src/loader.ts', 'src/components.ts'] : []), + ...(inputCssFile ? [inputCssFile] : []), ]; - const inputStyleFile = packageData.config?.css_source; - const buildCss = async () => { - if (!inputStyleFile) { - return; + const external = new Set([ + // We force "dependencies" and "peerDependencies" to be external to avoid bundling them. + ...Object.keys(packageData.dependencies || {}), + ...Object.keys(packageData.peerDependencies || {}), + ]); + + inputFiles.forEach((file) => { + // custom handling for StimulusBundle + if (file.includes('StimulusBundle/assets/src/loader.ts')) { + external.add('./controllers.js'); } - const inputStyleFileDist = path.resolve(distDir, `${path.basename(inputStyleFile, '.css')}.min.css`); - - console.log('Minifying CSS...'); - const css = await fs.promises.readFile(inputStyleFile, 'utf-8'); - const { code: minified } = LightningCSS.transform({ - filename: path.basename(inputStyleFile, '.css'), - code: Buffer.from(css), - minify: true, - sourceMap: false, // TODO: Maybe we can add source maps later? :) - }); - await fs.promises.writeFile(inputStyleFileDist, minified); - }; - - if (inputScriptFiles.length === 0) { - console.error( - `No input files found for package "${packageName}" (directory "${packageRoot}").\nEnsure you have at least a file matching the pattern "src/*_controller.ts", or manually specify input files in "${import.meta.filename}" file.` - ); - process.exit(1); - } - const rollupConfig = getRollupConfiguration({ - packageRoot, - inputFiles: inputScriptFiles, - isWatch, - additionalPlugins: [ - ...(isWatch && inputStyleFile - ? [ - { - name: 'watcher', - buildStart(this: rollup.PluginContext) { - this.addWatchFile(inputStyleFile); - }, - }, - ] - : []), - ], + // React, Vue, Svelte + if (file.includes('assets/src/loader.ts')) { + external.add('./components.js'); + } }); - if (isWatch) { - console.log( - `Watching for JavaScript${inputStyleFile ? ' and CSS' : ''} files modifications in "${srcDir}" directory...` - ); - - const watcher = rollup.watch(rollupConfig); - watcher.on('event', (event) => { - if (event.code === 'ERROR') { - console.error('Error during build:', event.error); - } - - if ((event.code === 'BUNDLE_END' || event.code === 'ERROR') && event.result) { - event.result.close(); - } - }); - watcher.on('change', async (id, { event }) => { - if (event === 'update') { - console.log('Files were modified, rebuilding...'); - } - - if (inputStyleFile && id === inputStyleFile) { - await buildCss(); + await build({ + entry: inputFiles, + outDir: path.join(packageRoot, 'dist'), + clean: true, + external: Array.from(external), + format: 'esm', + platform: 'browser', + tsconfig: path.join(import.meta.dirname, '../tsconfig.packages.json'), + dts: { + entry: inputFiles.filter(inputFile => !inputFile.endsWith('.css')), + }, + watch: isWatch, + splitting: false, + esbuildOptions(options) { + // Disabling `bundle` option prevent esbuild to inline relative (but external) imports (like "./components.js" for React, Vue, Svelte). + options.bundle = !(isStimulusBundle || isReactOrVueOrSvelte); + }, + plugins: [ + { + /** + * This plugin is used to minify CSS files using LightningCSS. + * + * Even if tsup supports CSS minification through ESBuild by setting the `minify: true` option, + * it also minifies JS files but we don't want that. + */ + name: 'symfony-ux:minify-css', + async renderChunk(code, chunkInfo) { + if (!/\.css$/.test(chunkInfo.path)) { + return null; + } + + const { transform } = await import('lightningcss'); + const result = transform({ + filename: chunkInfo.path, + code: Buffer.from(code), + minify: true, + }); + + console.log(`[Symfony UX] Minified CSS file: ${chunkInfo.path}`); + + return { + code: result.code.toString(), + map: result.map ? result.map.toString() : null, + } + }, + }, + + /** + * Unlike tsdown/rolldown and the option "cssEntryFileNames", tsup does not support + * customizing the output file names for CSS files. + * A plugin is needed to rename the written CSS files to add the ".min" suffix. + */ + { + name: 'symfony-ux:append-min-to-css', + async buildEnd({ writtenFiles }) { + for (const writtenFile of writtenFiles) { + if (!writtenFile.name.endsWith('.css')) { + continue; + } + + const newName = writtenFile.name.replace(/\.css$/, '.min.css'); + await fs.promises.rename(writtenFile.name, newName); + + console.info(`[Symfony UX] Renamed ${writtenFile.name} to ${newName}`); + } + } } - }); - } else { - console.log(`Building JavaScript files from ${packageName} package...`); - const start = Date.now(); - - if (typeof rollupConfig.output === 'undefined' || Array.isArray(rollupConfig.output)) { - console.error( - `The rollup configuration for package "${packageName}" does not contain a valid output configuration.` - ); - process.exit(1); - } - - const bundle = await rollup.rollup(rollupConfig); - await bundle.write(rollupConfig.output); - - await buildCss(); - - console.log(`Done in ${((Date.now() - start) / 1000).toFixed(3)} seconds.`); - } + ], + }); } main(); diff --git a/package.json b/package.json index 92f574c8434..47246ef5d10 100644 --- a/package.json +++ b/package.json @@ -14,16 +14,14 @@ }, "devDependencies": { "@biomejs/biome": "^2.0.4", - "@rollup/plugin-commonjs": "^28.0.6", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-typescript": "^12.1.4", "@testing-library/dom": "catalog:", "@testing-library/jest-dom": "catalog:", "@types/node": "^22.6.0", "lightningcss": "^1.28.2", + "pkg-types": "^2.2.0", "playwright": "^1.47.0", - "rollup": "^4.44.1", "tinyglobby": "^0.2.14", + "tsup": "^8.5.0", "vitest": "catalog:" }, "version": "2.27.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80d495e19ac..1811efb3955 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,15 +49,6 @@ importers: '@biomejs/biome': specifier: ^2.0.4 version: 2.1.2 - '@rollup/plugin-commonjs': - specifier: ^28.0.6 - version: 28.0.6(rollup@4.45.1) - '@rollup/plugin-node-resolve': - specifier: ^16.0.1 - version: 16.0.1(rollup@4.45.1) - '@rollup/plugin-typescript': - specifier: ^12.1.4 - version: 12.1.4(rollup@4.45.1)(tslib@2.8.1)(typescript@5.8.3) '@testing-library/dom': specifier: 'catalog:' version: 10.4.0 @@ -70,15 +61,18 @@ importers: lightningcss: specifier: ^1.28.2 version: 1.30.1 + pkg-types: + specifier: ^2.2.0 + version: 2.2.0 playwright: specifier: ^1.47.0 version: 1.54.1 - rollup: - specifier: ^4.44.1 - version: 4.45.1 tinyglobby: specifier: ^0.2.14 version: 0.2.14 + tsup: + specifier: ^8.5.0 + version: 8.5.0(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3) vitest: specifier: 'catalog:' version: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(jsdom@26.1.0)(lightningcss@1.30.1)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(terser@5.43.1) @@ -1316,6 +1310,10 @@ packages: '@types/node': optional: true + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jest/types@26.6.2': resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} engines: {node: '>= 10.14.2'} @@ -1358,52 +1356,16 @@ packages: '@orchidjs/unicode-variants@1.1.2': resolution: {integrity: sha512-5DobW1CHgnBROOEpFlEXytED5OosEWESFvg/VYmH0143oXcijYTprRYJTs+55HzGM4IqxiLFSuqEzu9mPNwVsA==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - '@rollup/plugin-commonjs@28.0.6': - resolution: {integrity: sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==} - engines: {node: '>=16.0.0 || 14 >= 14.17'} - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-node-resolve@16.0.1': - resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.78.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-typescript@12.1.4': - resolution: {integrity: sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.14.0||^3.0.0||^4.0.0 - tslib: '*' - typescript: '>=3.7.0' - peerDependenciesMeta: - rollup: - optional: true - tslib: - optional: true - - '@rollup/pluginutils@5.2.0': - resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - '@rollup/rollup-android-arm-eabi@4.45.1': resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} cpu: [arm] @@ -1643,9 +1605,6 @@ packages: '@types/react@18.3.23': resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} - '@types/resolve@1.20.2': - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} @@ -1780,6 +1739,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1788,6 +1751,13 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + aria-query@4.2.2: resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} engines: {node: '>=6.0'} @@ -1810,6 +1780,12 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1818,6 +1794,12 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1853,6 +1835,10 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -1878,8 +1864,19 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1897,6 +1894,10 @@ packages: cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + css-tree@2.3.1: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -1963,12 +1964,18 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.187: resolution: {integrity: sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2020,6 +2027,9 @@ packages: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2034,6 +2044,13 @@ packages: picomatch: optional: true + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} @@ -2070,6 +2087,10 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2123,32 +2144,32 @@ packages: intl-messageformat@10.7.16: resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jest-canvas-mock@2.5.2: resolution: {integrity: sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2248,6 +2269,17 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + loader-utils@3.3.1: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} @@ -2255,6 +2287,9 @@ packages: locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2297,6 +2332,17 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + moo-color@1.0.3: resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==} @@ -2321,6 +2367,9 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2341,6 +2390,10 @@ packages: nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + opencollective-postinstall@2.0.3: resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==} hasBin: true @@ -2348,11 +2401,19 @@ packages: outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -2374,6 +2435,16 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + playwright-core@1.54.1: resolution: {integrity: sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==} engines: {node: '>=18'} @@ -2384,6 +2455,24 @@ packages: engines: {node: '>=18'} hasBin: true + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -2422,6 +2511,10 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -2440,14 +2533,13 @@ packages: resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - rollup@4.45.1: resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2474,6 +2566,14 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -2496,6 +2596,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2513,10 +2617,18 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -2524,14 +2636,15 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - svelte-hmr@0.15.3: resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} engines: {node: ^12.20 || ^14.13.1 || >= 16} @@ -2553,6 +2666,13 @@ packages: engines: {node: '>=10'} hasBin: true + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2600,13 +2720,42 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.5.0: + resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + tsx@4.20.3: resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} @@ -2631,6 +2780,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -2745,6 +2897,9 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -2764,6 +2919,14 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -2777,6 +2940,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -3234,6 +3401,15 @@ snapshots: '@types/node': 22.16.5 optional: true + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jest/types@26.6.2': dependencies: '@types/istanbul-lib-coverage': 2.0.6 @@ -3292,49 +3468,13 @@ snapshots: '@orchidjs/unicode-variants@1.1.2': {} + '@pkgjs/parseargs@0.11.0': + optional: true + '@polka/url@1.0.0-next.29': {} '@rolldown/pluginutils@1.0.0-beta.27': {} - '@rollup/plugin-commonjs@28.0.6(rollup@4.45.1)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - commondir: 1.0.1 - estree-walker: 2.0.2 - fdir: 6.4.6(picomatch@4.0.3) - is-reference: 1.2.1 - magic-string: 0.30.17 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.45.1 - - '@rollup/plugin-node-resolve@16.0.1(rollup@4.45.1)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - '@types/resolve': 1.20.2 - deepmerge: 4.3.1 - is-module: 1.0.0 - resolve: 1.22.10 - optionalDependencies: - rollup: 4.45.1 - - '@rollup/plugin-typescript@12.1.4(rollup@4.45.1)(tslib@2.8.1)(typescript@5.8.3)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - resolve: 1.22.10 - typescript: 5.8.3 - optionalDependencies: - rollup: 4.45.1 - tslib: 2.8.1 - - '@rollup/pluginutils@5.2.0(rollup@4.45.1)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.45.1 - '@rollup/rollup-android-arm-eabi@4.45.1': optional: true @@ -3576,8 +3716,6 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.1.3 - '@types/resolve@1.20.2': {} - '@types/statuses@2.0.6': optional: true @@ -3752,12 +3890,18 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.1.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + aria-query@4.2.2: dependencies: '@babel/runtime': 7.27.6 @@ -3775,6 +3919,12 @@ snapshots: axobject-query@4.1.0: {} + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001727 @@ -3785,6 +3935,11 @@ snapshots: buffer-from@1.1.2: optional: true + bundle-require@5.1.0(esbuild@0.25.8): + dependencies: + esbuild: 0.25.8 + load-tsconfig: 0.2.5 + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -3820,6 +3975,10 @@ snapshots: check-error@2.1.1: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + cli-width@4.1.0: optional: true @@ -3851,7 +4010,13 @@ snapshots: commander@2.20.3: optional: true - commondir@1.0.1: {} + commander@4.1.1: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + consola@3.4.2: {} convert-source-map@2.0.0: {} @@ -3868,6 +4033,12 @@ snapshots: transitivePeerDependencies: - encoding + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 @@ -3919,10 +4090,13 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.187: {} - emoji-regex@8.0.0: - optional: true + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} entities@4.5.0: {} @@ -4010,6 +4184,8 @@ snapshots: expect-type@1.2.2: {} + exsolve@1.0.7: {} + fast-deep-equal@3.1.3: {} fast-uri@3.0.6: {} @@ -4018,6 +4194,17 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.17 + mlly: 1.7.4 + rollup: 4.45.1 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -4061,6 +4248,15 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + gopd@1.2.0: {} graphql@16.11.0: @@ -4114,33 +4310,32 @@ snapshots: '@formatjs/icu-messageformat-parser': 2.11.2 tslib: 2.8.1 - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-fullwidth-code-point@3.0.0: - optional: true - - is-module@1.0.0: {} + is-fullwidth-code-point@3.0.0: {} is-node-process@1.2.0: optional: true is-potential-custom-element-name@1.0.1: {} - is-reference@1.2.1: + is-reference@3.0.3: dependencies: '@types/estree': 1.0.8 - is-reference@3.0.3: + isexe@2.0.0: {} + + jackspeak@3.4.3: dependencies: - '@types/estree': 1.0.8 + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 jest-canvas-mock@2.5.2: dependencies: cssfontparser: 1.2.1 moo-color: 1.0.3 + joycon@3.1.1: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -4227,10 +4422,18 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + loader-utils@3.3.1: {} locate-character@3.0.0: {} + lodash.sortby@4.7.0: {} + lodash@4.17.21: {} loose-envify@1.4.0: @@ -4263,6 +4466,19 @@ snapshots: min-indent@1.0.1: {} + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + moo-color@1.0.3: dependencies: color-name: 1.1.4 @@ -4300,6 +4516,12 @@ snapshots: mute-stream@2.0.0: optional: true + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} node-fetch@2.7.0: @@ -4310,16 +4532,25 @@ snapshots: nwsapi@2.2.20: {} + object-assign@4.1.1: {} + opencollective-postinstall@2.0.3: {} outvariant@1.4.3: optional: true + package-json-from-dist@1.0.1: {} + parse5@7.3.0: dependencies: entities: 6.0.1 - path-parse@1.0.7: {} + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 path-to-regexp@6.3.0: optional: true @@ -4338,6 +4569,20 @@ snapshots: picomatch@4.0.3: {} + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.2.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + playwright-core@1.54.1: {} playwright@1.54.1: @@ -4346,6 +4591,13 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + postcss-load-config@6.0.1(postcss@8.5.6)(tsx@4.20.3): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.6 + tsx: 4.20.3 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -4389,6 +4641,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + readdirp@4.1.2: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -4404,13 +4658,9 @@ snapshots: resize-observer-polyfill@1.5.1: {} - resolve-pkg-maps@1.0.0: {} + resolve-from@5.0.0: {} - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + resolve-pkg-maps@1.0.0: {} rollup@4.45.1: dependencies: @@ -4459,10 +4709,15 @@ snapshots: semver@6.3.1: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + siginfo@2.0.0: {} - signal-exit@4.1.0: - optional: true + signal-exit@4.1.0: {} sirv@3.0.1: dependencies: @@ -4481,6 +4736,10 @@ snapshots: source-map@0.6.1: optional: true + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + stackback@0.0.2: {} statuses@2.0.2: @@ -4496,12 +4755,20 @@ snapshots: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - optional: true + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - optional: true + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 strip-indent@3.0.0: dependencies: @@ -4511,12 +4778,20 @@ snapshots: dependencies: js-tokens: 9.0.1 + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - svelte-hmr@0.15.3(svelte@4.2.20): dependencies: svelte: 4.2.20 @@ -4553,6 +4828,14 @@ snapshots: source-map-support: 0.5.21 optional: true + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -4595,12 +4878,48 @@ snapshots: tr46@0.0.3: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + tr46@5.1.1: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + tslib@2.8.1: {} + tsup@8.5.0(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.25.8) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.1 + esbuild: 0.25.8 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@4.20.3) + resolve-from: 5.0.0 + rollup: 4.45.1 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.6 + typescript: 5.8.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + tsx@4.20.3: dependencies: esbuild: 0.25.8 @@ -4620,6 +4939,8 @@ snapshots: typescript@5.8.3: {} + ufo@1.6.1: {} + undici-types@6.21.0: {} universalify@0.2.0: @@ -4738,6 +5059,8 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: @@ -4756,6 +5079,16 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -4773,7 +5106,12 @@ snapshots: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - optional: true + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 ws@8.18.3: {} diff --git a/src/Autocomplete/assets/dist/controller.d.ts b/src/Autocomplete/assets/dist/controller.d.ts index d5cc6b3e272..e03b841b16d 100644 --- a/src/Autocomplete/assets/dist/controller.d.ts +++ b/src/Autocomplete/assets/dist/controller.d.ts @@ -1,13 +1,14 @@ import { Controller } from '@hotwired/stimulus'; import TomSelect from 'tom-select'; -export interface AutocompletePreConnectOptions { + +interface AutocompletePreConnectOptions { options: any; } -export interface AutocompleteConnectOptions { +interface AutocompleteConnectOptions { tomSelect: TomSelect; options: any; } -export default class extends Controller { +declare class export_default extends Controller { #private; static values: { url: StringConstructor; @@ -54,3 +55,5 @@ export default class extends Controller { private createOptionsDataStructure; private areOptionsEquivalent; } + +export { type AutocompleteConnectOptions, type AutocompletePreConnectOptions, export_default as default }; diff --git a/src/Autocomplete/assets/dist/controller.js b/src/Autocomplete/assets/dist/controller.js index c68b7bd3015..a96549af0a4 100644 --- a/src/Autocomplete/assets/dist/controller.js +++ b/src/Autocomplete/assets/dist/controller.js @@ -1,417 +1,412 @@ -import { Controller } from '@hotwired/stimulus'; -import TomSelect from 'tom-select'; - -/****************************************************************************** -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 */ - - -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); -} - -typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { - var e = new Error(message); - return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +var __typeError = (msg) => { + throw TypeError(msg); }; +var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); +var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); +var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); -var _default_1_instances, _default_1_getCommonConfig, _default_1_createAutocomplete, _default_1_createAutocompleteWithHtmlContents, _default_1_createAutocompleteWithRemoteData, _default_1_stripTags, _default_1_mergeConfigs, _default_1_normalizePluginsToHash, _default_1_normalizePlugins, _default_1_createTomSelect; -class default_1 extends Controller { - constructor() { - super(...arguments); - _default_1_instances.add(this); - this.isObserving = false; - this.hasLoadedChoicesPreviously = false; - this.originalOptions = []; - _default_1_normalizePluginsToHash.set(this, (plugins) => { - if (Array.isArray(plugins)) { - return plugins.reduce((acc, plugin) => { - if (typeof plugin === 'string') { - acc[plugin] = {}; - } - if (typeof plugin === 'object' && plugin.name) { - acc[plugin.name] = plugin.options || {}; - } - return acc; - }, {}); - } - return plugins; - }); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +import TomSelect from "tom-select"; +var _instances, getCommonConfig_fn, createAutocomplete_fn, createAutocompleteWithHtmlContents_fn, createAutocompleteWithRemoteData_fn, stripTags_fn, mergeConfigs_fn, _normalizePluginsToHash, normalizePlugins_fn, createTomSelect_fn; +var controller_default = class extends Controller { + constructor() { + super(...arguments); + __privateAdd(this, _instances); + this.isObserving = false; + this.hasLoadedChoicesPreviously = false; + this.originalOptions = []; + /** + * Normalizes the plugins to a hash, so that we can merge them easily. + */ + __privateAdd(this, _normalizePluginsToHash, (plugins) => { + if (Array.isArray(plugins)) { + return plugins.reduce((acc, plugin) => { + if (typeof plugin === "string") { + acc[plugin] = {}; + } + if (typeof plugin === "object" && plugin.name) { + acc[plugin.name] = plugin.options || {}; + } + return acc; + }, {}); + } + return plugins; + }); + } + initialize() { + if (!this.mutationObserver) { + this.mutationObserver = new MutationObserver((mutations) => { + this.onMutations(mutations); + }); } - initialize() { - if (!this.mutationObserver) { - this.mutationObserver = new MutationObserver((mutations) => { - this.onMutations(mutations); - }); - } + } + connect() { + if (this.selectElement) { + this.originalOptions = this.createOptionsDataStructure(this.selectElement); } - connect() { - if (this.selectElement) { - this.originalOptions = this.createOptionsDataStructure(this.selectElement); - } - this.initializeTomSelect(); + this.initializeTomSelect(); + } + initializeTomSelect() { + if (this.selectElement) { + this.selectElement.setAttribute("data-skip-morph", ""); } - initializeTomSelect() { - if (this.selectElement) { - this.selectElement.setAttribute('data-skip-morph', ''); - } - if (this.urlValue) { - this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithRemoteData).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null); - return; - } - if (this.optionsAsHtmlValue) { - this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithHtmlContents).call(this); - return; - } - this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this); - this.startMutationObserver(); + if (this.urlValue) { + this.tomSelect = __privateMethod(this, _instances, createAutocompleteWithRemoteData_fn).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null); + return; } - disconnect() { - this.stopMutationObserver(); - let currentSelectedValues = []; - if (this.selectElement) { - if (this.selectElement.multiple) { - currentSelectedValues = Array.from(this.selectElement.options) - .filter((option) => option.selected) - .map((option) => option.value); - } - else { - currentSelectedValues = [this.selectElement.value]; - } - } - this.tomSelect.destroy(); - if (this.selectElement) { - if (this.selectElement.multiple) { - Array.from(this.selectElement.options).forEach((option) => { - option.selected = currentSelectedValues.includes(option.value); - }); - } - else { - this.selectElement.value = currentSelectedValues[0]; - } - } + if (this.optionsAsHtmlValue) { + this.tomSelect = __privateMethod(this, _instances, createAutocompleteWithHtmlContents_fn).call(this); + return; } - urlValueChanged() { - this.resetTomSelect(); + this.tomSelect = __privateMethod(this, _instances, createAutocomplete_fn).call(this); + this.startMutationObserver(); + } + disconnect() { + this.stopMutationObserver(); + let currentSelectedValues = []; + if (this.selectElement) { + if (this.selectElement.multiple) { + currentSelectedValues = Array.from(this.selectElement.options).filter((option) => option.selected).map((option) => option.value); + } else { + currentSelectedValues = [this.selectElement.value]; + } } - getMaxOptions() { - return this.selectElement ? this.selectElement.options.length : 50; + this.tomSelect.destroy(); + if (this.selectElement) { + if (this.selectElement.multiple) { + Array.from(this.selectElement.options).forEach((option) => { + option.selected = currentSelectedValues.includes(option.value); + }); + } else { + this.selectElement.value = currentSelectedValues[0]; + } } - get selectElement() { - if (!(this.element instanceof HTMLSelectElement)) { - return null; - } - return this.element; + } + urlValueChanged() { + this.resetTomSelect(); + } + getMaxOptions() { + return this.selectElement ? this.selectElement.options.length : 50; + } + /** + * Returns the element, but only if it's a select element. + */ + get selectElement() { + if (!(this.element instanceof HTMLSelectElement)) { + return null; } - get formElement() { - if (!(this.element instanceof HTMLInputElement) && !(this.element instanceof HTMLSelectElement)) { - throw new Error('Autocomplete Stimulus controller can only be used on an or .'); - } - return this.element; + return this.element; + } + /** + * Getter to help typing. + */ + get formElement() { + if (!(this.element instanceof HTMLInputElement) && !(this.element instanceof HTMLSelectElement)) { + throw new Error("Autocomplete Stimulus controller can only be used on an or ."); } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'autocomplete' }); + return this.element; + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "autocomplete" }); + } + get preload() { + if (!this.hasPreloadValue) { + return "focus"; } - get preload() { - if (!this.hasPreloadValue) { - return 'focus'; - } - if (this.preloadValue === 'false') { - return false; - } - if (this.preloadValue === 'true') { - return true; - } - return this.preloadValue; + if (this.preloadValue === "false") { + return false; } - resetTomSelect() { - if (this.tomSelect) { - this.dispatchEvent('before-reset', { tomSelect: this.tomSelect }); - this.stopMutationObserver(); - const currentHtml = this.element.innerHTML; - const currentValue = this.tomSelect.getValue(); - this.tomSelect.destroy(); - this.element.innerHTML = currentHtml; - this.initializeTomSelect(); - this.tomSelect.setValue(currentValue); - } + if (this.preloadValue === "true") { + return true; } - changeTomSelectDisabledState(isDisabled) { - this.stopMutationObserver(); - if (isDisabled) { - this.tomSelect.disable(); - } - else { - this.tomSelect.enable(); - } - this.startMutationObserver(); + return this.preloadValue; + } + resetTomSelect() { + if (this.tomSelect) { + this.dispatchEvent("before-reset", { tomSelect: this.tomSelect }); + this.stopMutationObserver(); + const currentHtml = this.element.innerHTML; + const currentValue = this.tomSelect.getValue(); + this.tomSelect.destroy(); + this.element.innerHTML = currentHtml; + this.initializeTomSelect(); + this.tomSelect.setValue(currentValue); } - startMutationObserver() { - if (!this.isObserving && this.mutationObserver) { - this.mutationObserver.observe(this.element, { - childList: true, - subtree: true, - attributes: true, - characterData: true, - attributeOldValue: true, - }); - this.isObserving = true; - } + } + changeTomSelectDisabledState(isDisabled) { + this.stopMutationObserver(); + if (isDisabled) { + this.tomSelect.disable(); + } else { + this.tomSelect.enable(); } - stopMutationObserver() { - if (this.isObserving && this.mutationObserver) { - this.mutationObserver.disconnect(); - this.isObserving = false; - } + this.startMutationObserver(); + } + startMutationObserver() { + if (!this.isObserving && this.mutationObserver) { + this.mutationObserver.observe(this.element, { + childList: true, + subtree: true, + attributes: true, + characterData: true, + attributeOldValue: true + }); + this.isObserving = true; } - onMutations(mutations) { - let changeDisabledState = false; - let requireReset = false; - mutations.forEach((mutation) => { - switch (mutation.type) { - case 'attributes': - if (mutation.target === this.element && mutation.attributeName === 'disabled') { - changeDisabledState = true; - break; - } - if (mutation.target === this.element && mutation.attributeName === 'multiple') { - const isNowMultiple = this.element.hasAttribute('multiple'); - const wasMultiple = mutation.oldValue === 'multiple'; - if (isNowMultiple !== wasMultiple) { - requireReset = true; - } - break; - } - break; + } + stopMutationObserver() { + if (this.isObserving && this.mutationObserver) { + this.mutationObserver.disconnect(); + this.isObserving = false; + } + } + onMutations(mutations) { + let changeDisabledState = false; + let requireReset = false; + mutations.forEach((mutation) => { + switch (mutation.type) { + case "attributes": + if (mutation.target === this.element && mutation.attributeName === "disabled") { + changeDisabledState = true; + break; + } + if (mutation.target === this.element && mutation.attributeName === "multiple") { + const isNowMultiple = this.element.hasAttribute("multiple"); + const wasMultiple = mutation.oldValue === "multiple"; + if (isNowMultiple !== wasMultiple) { + requireReset = true; } - }); - const newOptions = this.selectElement ? this.createOptionsDataStructure(this.selectElement) : []; - const areOptionsEquivalent = this.areOptionsEquivalent(newOptions); - if (!areOptionsEquivalent || requireReset) { - this.originalOptions = newOptions; - this.resetTomSelect(); - } - if (changeDisabledState) { - this.changeTomSelectDisabledState(this.formElement.disabled); - } + break; + } + break; + } + }); + const newOptions = this.selectElement ? this.createOptionsDataStructure(this.selectElement) : []; + const areOptionsEquivalent = this.areOptionsEquivalent(newOptions); + if (!areOptionsEquivalent || requireReset) { + this.originalOptions = newOptions; + this.resetTomSelect(); } - createOptionsDataStructure(selectElement) { - return Array.from(selectElement.options).map((option) => { - return { - value: option.value, - text: option.text, - }; - }); + if (changeDisabledState) { + this.changeTomSelectDisabledState(this.formElement.disabled); } - areOptionsEquivalent(newOptions) { - const filteredOriginalOptions = this.originalOptions.filter((option) => option.value !== ''); - const filteredNewOptions = newOptions.filter((option) => option.value !== ''); - const originalPlaceholderOption = this.originalOptions.find((option) => option.value === ''); - const newPlaceholderOption = newOptions.find((option) => option.value === ''); - if (originalPlaceholderOption && - newPlaceholderOption && - originalPlaceholderOption.text !== newPlaceholderOption.text) { - return false; - } - if (filteredOriginalOptions.length !== filteredNewOptions.length) { - return false; - } - const normalizeOption = (option) => `${option.value}-${option.text}`; - const originalOptionsSet = new Set(filteredOriginalOptions.map(normalizeOption)); - const newOptionsSet = new Set(filteredNewOptions.map(normalizeOption)); - return (originalOptionsSet.size === newOptionsSet.size && - [...originalOptionsSet].every((option) => newOptionsSet.has(option))); + } + createOptionsDataStructure(selectElement) { + return Array.from(selectElement.options).map((option) => { + return { + value: option.value, + text: option.text + }; + }); + } + areOptionsEquivalent(newOptions) { + const filteredOriginalOptions = this.originalOptions.filter((option) => option.value !== ""); + const filteredNewOptions = newOptions.filter((option) => option.value !== ""); + const originalPlaceholderOption = this.originalOptions.find((option) => option.value === ""); + const newPlaceholderOption = newOptions.find((option) => option.value === ""); + if (originalPlaceholderOption && newPlaceholderOption && originalPlaceholderOption.text !== newPlaceholderOption.text) { + return false; } -} -_default_1_normalizePluginsToHash = new WeakMap(), _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _default_1_getCommonConfig() { - const plugins = {}; - const isMultiple = !this.selectElement || this.selectElement.multiple; - if (!this.formElement.disabled && !isMultiple) { - plugins.clear_button = { title: '' }; + if (filteredOriginalOptions.length !== filteredNewOptions.length) { + return false; } - if (isMultiple) { - plugins.remove_button = { title: '' }; + const normalizeOption = (option) => `${option.value}-${option.text}`; + const originalOptionsSet = new Set(filteredOriginalOptions.map(normalizeOption)); + const newOptionsSet = new Set(filteredNewOptions.map(normalizeOption)); + return originalOptionsSet.size === newOptionsSet.size && [...originalOptionsSet].every((option) => newOptionsSet.has(option)); + } +}; +_instances = new WeakSet(); +getCommonConfig_fn = function() { + const plugins = {}; + const isMultiple = !this.selectElement || this.selectElement.multiple; + if (!this.formElement.disabled && !isMultiple) { + plugins.clear_button = { title: "" }; + } + if (isMultiple) { + plugins.remove_button = { title: "" }; + } + if (this.urlValue) { + plugins.virtual_scroll = {}; + } + const render = { + no_results: () => { + return `${this.noResultsFoundTextValue}`; + }, + option_create: (data, escapeData) => { + return `${this.createOptionTextValue.replace("%placeholder%", `${escapeData(data.input)}`)}`; } - if (this.urlValue) { - plugins.virtual_scroll = {}; + }; + const config = { + render, + plugins, + // clear the text input after selecting a value + onItemAdd: () => { + this.tomSelect.setTextboxValue(""); + }, + closeAfterSelect: true, + // fix positioning (in the dropdown) of options added through addOption() + onOptionAdd: (value, data) => { + let parentElement = this.tomSelect.input; + let optgroupData = null; + const optgroup = data[this.tomSelect.settings.optgroupField]; + if (optgroup && this.tomSelect.optgroups) { + optgroupData = this.tomSelect.optgroups[optgroup]; + if (optgroupData) { + const optgroupElement = parentElement.querySelector(`optgroup[label="${optgroupData.label}"]`); + if (optgroupElement) { + parentElement = optgroupElement; + } + } + } + const optionElement = document.createElement("option"); + optionElement.value = value; + optionElement.text = data[this.tomSelect.settings.labelField]; + const optionOrder = data.$order; + let orderedOption = null; + for (const [, tomSelectOption] of Object.entries(this.tomSelect.options)) { + if (tomSelectOption.$order === optionOrder) { + orderedOption = parentElement.querySelector( + `:scope > option[value="${CSS.escape(tomSelectOption[this.tomSelect.settings.valueField])}"]` + ); + break; + } + } + if (orderedOption) { + orderedOption.insertAdjacentElement("afterend", optionElement); + } else if (optionOrder >= 0) { + parentElement.append(optionElement); + } else { + parentElement.prepend(optionElement); + } } - const render = { - no_results: () => { - return `${this.noResultsFoundTextValue}`; - }, - option_create: (data, escapeData) => { - return `${this.createOptionTextValue.replace('%placeholder%', `${escapeData(data.input)}`)}`; - }, - }; - const config = { - render, - plugins, - onItemAdd: () => { - this.tomSelect.setTextboxValue(''); - }, - closeAfterSelect: true, - onOptionAdd: (value, data) => { - let parentElement = this.tomSelect.input; - let optgroupData = null; - const optgroup = data[this.tomSelect.settings.optgroupField]; - if (optgroup && this.tomSelect.optgroups) { - optgroupData = this.tomSelect.optgroups[optgroup]; - if (optgroupData) { - const optgroupElement = parentElement.querySelector(`optgroup[label="${optgroupData.label}"]`); - if (optgroupElement) { - parentElement = optgroupElement; - } - } - } - const optionElement = document.createElement('option'); - optionElement.value = value; - optionElement.text = data[this.tomSelect.settings.labelField]; - const optionOrder = data.$order; - let orderedOption = null; - for (const [, tomSelectOption] of Object.entries(this.tomSelect.options)) { - if (tomSelectOption.$order === optionOrder) { - orderedOption = parentElement.querySelector(`:scope > option[value="${CSS.escape(tomSelectOption[this.tomSelect.settings.valueField])}"]`); - break; - } - } - if (orderedOption) { - orderedOption.insertAdjacentElement('afterend', optionElement); - } - else if (optionOrder >= 0) { - parentElement.append(optionElement); - } - else { - parentElement.prepend(optionElement); - } - }, - }; - if (!this.selectElement && !this.urlValue) { - config.shouldLoad = () => false; + }; + if (!this.selectElement && !this.urlValue) { + config.shouldLoad = () => false; + } + return __privateMethod(this, _instances, mergeConfigs_fn).call(this, config, this.tomSelectOptionsValue); +}; +createAutocomplete_fn = function() { + const config = __privateMethod(this, _instances, mergeConfigs_fn).call(this, __privateMethod(this, _instances, getCommonConfig_fn).call(this), { + maxOptions: this.getMaxOptions() + }); + return __privateMethod(this, _instances, createTomSelect_fn).call(this, config); +}; +createAutocompleteWithHtmlContents_fn = function() { + const commonConfig = __privateMethod(this, _instances, getCommonConfig_fn).call(this); + const labelField = commonConfig.labelField ?? "text"; + const config = __privateMethod(this, _instances, mergeConfigs_fn).call(this, commonConfig, { + maxOptions: this.getMaxOptions(), + score: (search) => { + const scoringFunction = this.tomSelect.getScoreFunction(search); + return (item) => { + return scoringFunction({ ...item, text: __privateMethod(this, _instances, stripTags_fn).call(this, item[labelField]) }); + }; + }, + render: { + item: (item) => `${item[labelField]}`, + option: (item) => `${item[labelField]}` } - return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, config, this.tomSelectOptionsValue); -}, _default_1_createAutocomplete = function _default_1_createAutocomplete() { - const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this), { - maxOptions: this.getMaxOptions(), - }); - return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config); -}, _default_1_createAutocompleteWithHtmlContents = function _default_1_createAutocompleteWithHtmlContents() { - const commonConfig = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this); - const labelField = commonConfig.labelField ?? 'text'; - const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, commonConfig, { - maxOptions: this.getMaxOptions(), - score: (search) => { - const scoringFunction = this.tomSelect.getScoreFunction(search); - return (item) => { - return scoringFunction({ ...item, text: __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_stripTags).call(this, item[labelField]) }); - }; - }, - render: { - item: (item) => `${item[labelField]}`, - option: (item) => `${item[labelField]}`, - }, - }); - return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config); -}, _default_1_createAutocompleteWithRemoteData = function _default_1_createAutocompleteWithRemoteData(autocompleteEndpointUrl, minCharacterLength) { - const commonConfig = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this); - const labelField = commonConfig.labelField ?? 'text'; - const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, commonConfig, { - firstUrl: (query) => { - const separator = autocompleteEndpointUrl.includes('?') ? '&' : '?'; - return `${autocompleteEndpointUrl}${separator}query=${encodeURIComponent(query)}`; - }, - load: function (query, callback) { - const url = this.getUrl(query); - fetch(url) - .then((response) => response.json()) - .then((json) => { - this.setNextUrl(query, json.next_page); - callback(json.results.options || json.results, json.results.optgroups || []); - }) - .catch(() => callback([], [])); - }, - shouldLoad: (query) => { - if (null !== minCharacterLength) { - return query.length >= minCharacterLength; - } - if (this.hasLoadedChoicesPreviously) { - return true; - } - if (query.length > 0) { - this.hasLoadedChoicesPreviously = true; - } - return query.length >= 3; - }, - optgroupField: 'group_by', - score: (search) => (item) => 1, - render: { - option: (item) => `${item[labelField]}`, - item: (item) => `${item[labelField]}`, - loading_more: () => { - return `${this.loadingMoreTextValue}`; - }, - no_more_results: () => { - return `${this.noMoreResultsTextValue}`; - }, - no_results: () => { - return `${this.noResultsFoundTextValue}`; - }, - option_create: (data, escapeData) => { - return `${this.createOptionTextValue.replace('%placeholder%', `${escapeData(data.input)}`)}`; - }, - }, - preload: this.preload, - }); - return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config); -}, _default_1_stripTags = function _default_1_stripTags(string) { - return string.replace(/(<([^>]+)>)/gi, ''); -}, _default_1_mergeConfigs = function _default_1_mergeConfigs(config1, config2) { - return { - ...config1, - ...config2, - plugins: __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_normalizePlugins).call(this, { - ...__classPrivateFieldGet(this, _default_1_normalizePluginsToHash, "f").call(this, config1.plugins || {}), - ...__classPrivateFieldGet(this, _default_1_normalizePluginsToHash, "f").call(this, config2.plugins || {}), - }), - }; -}, _default_1_normalizePlugins = function _default_1_normalizePlugins(plugins) { - return Object.entries(plugins).reduce((acc, [pluginName, pluginOptions]) => { - if (pluginOptions !== false) { - acc[pluginName] = pluginOptions; - } - return acc; - }, {}); -}, _default_1_createTomSelect = function _default_1_createTomSelect(options) { - const preConnectPayload = { options }; - this.dispatchEvent('pre-connect', preConnectPayload); - const tomSelect = new TomSelect(this.formElement, options); - const connectPayload = { tomSelect, options }; - this.dispatchEvent('connect', connectPayload); - return tomSelect; + }); + return __privateMethod(this, _instances, createTomSelect_fn).call(this, config); }; -default_1.values = { - url: String, - optionsAsHtml: Boolean, - loadingMoreText: String, - noResultsFoundText: String, - noMoreResultsText: String, - createOptionText: String, - minCharacters: Number, - tomSelectOptions: Object, - preload: String, +createAutocompleteWithRemoteData_fn = function(autocompleteEndpointUrl, minCharacterLength) { + const commonConfig = __privateMethod(this, _instances, getCommonConfig_fn).call(this); + const labelField = commonConfig.labelField ?? "text"; + const config = __privateMethod(this, _instances, mergeConfigs_fn).call(this, commonConfig, { + firstUrl: (query) => { + const separator = autocompleteEndpointUrl.includes("?") ? "&" : "?"; + return `${autocompleteEndpointUrl}${separator}query=${encodeURIComponent(query)}`; + }, + // VERY IMPORTANT: use 'function (query, callback) { ... }' instead of the + // '(query, callback) => { ... }' syntax because, otherwise, + // the 'this.XXX' calls inside this method fail + load: function(query, callback) { + const url = this.getUrl(query); + fetch(url).then((response) => response.json()).then((json) => { + this.setNextUrl(query, json.next_page); + callback(json.results.options || json.results, json.results.optgroups || []); + }).catch(() => callback([], [])); + }, + shouldLoad: (query) => { + if (null !== minCharacterLength) { + return query.length >= minCharacterLength; + } + if (this.hasLoadedChoicesPreviously) { + return true; + } + if (query.length > 0) { + this.hasLoadedChoicesPreviously = true; + } + return query.length >= 3; + }, + optgroupField: "group_by", + // avoid extra filtering after results are returned + score: (search) => (item) => 1, + render: { + option: (item) => `${item[labelField]}`, + item: (item) => `${item[labelField]}`, + loading_more: () => { + return `${this.loadingMoreTextValue}`; + }, + no_more_results: () => { + return `${this.noMoreResultsTextValue}`; + }, + no_results: () => { + return `${this.noResultsFoundTextValue}`; + }, + option_create: (data, escapeData) => { + return `${this.createOptionTextValue.replace("%placeholder%", `${escapeData(data.input)}`)}`; + } + }, + preload: this.preload + }); + return __privateMethod(this, _instances, createTomSelect_fn).call(this, config); +}; +stripTags_fn = function(string) { + return string.replace(/(<([^>]+)>)/gi, ""); +}; +mergeConfigs_fn = function(config1, config2) { + return { + ...config1, + ...config2, + // Plugins from both configs should be merged together. + plugins: __privateMethod(this, _instances, normalizePlugins_fn).call(this, { + ...__privateGet(this, _normalizePluginsToHash).call(this, config1.plugins || {}), + ...__privateGet(this, _normalizePluginsToHash).call(this, config2.plugins || {}) + }) + }; +}; +_normalizePluginsToHash = new WeakMap(); +normalizePlugins_fn = function(plugins) { + return Object.entries(plugins).reduce((acc, [pluginName, pluginOptions]) => { + if (pluginOptions !== false) { + acc[pluginName] = pluginOptions; + } + return acc; + }, {}); +}; +createTomSelect_fn = function(options) { + const preConnectPayload = { options }; + this.dispatchEvent("pre-connect", preConnectPayload); + const tomSelect = new TomSelect(this.formElement, options); + const connectPayload = { tomSelect, options }; + this.dispatchEvent("connect", connectPayload); + return tomSelect; +}; +controller_default.values = { + url: String, + optionsAsHtml: Boolean, + loadingMoreText: String, + noResultsFoundText: String, + noMoreResultsText: String, + createOptionText: String, + minCharacters: Number, + tomSelectOptions: Object, + preload: String +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/Chartjs/assets/dist/controller.d.ts b/src/Chartjs/assets/dist/controller.d.ts index bc05560d88b..5f5d266b6de 100644 --- a/src/Chartjs/assets/dist/controller.d.ts +++ b/src/Chartjs/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { readonly viewValue: any; static values: { view: ObjectConstructor; @@ -10,3 +11,5 @@ export default class extends Controller { viewValueChanged(): void; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/Chartjs/assets/dist/controller.js b/src/Chartjs/assets/dist/controller.js index 7caa0cb1b8e..ab38188373c 100644 --- a/src/Chartjs/assets/dist/controller.js +++ b/src/Chartjs/assets/dist/controller.js @@ -1,73 +1,77 @@ -import { Controller } from '@hotwired/stimulus'; -import { registerables, Chart } from 'chart.js'; - +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +import { Chart, registerables } from "chart.js"; if (registerables) { - Chart.register(...registerables); + Chart.register(...registerables); } -let isChartInitialized = false; -class default_1 extends Controller { - constructor() { - super(...arguments); - this.chart = null; +var isChartInitialized = false; +var controller_default = class extends Controller { + constructor() { + super(...arguments); + this.chart = null; + } + connect() { + if (!isChartInitialized) { + isChartInitialized = true; + this.dispatchEvent("init", { + Chart + }); } - connect() { - if (!isChartInitialized) { - isChartInitialized = true; - this.dispatchEvent('init', { - Chart, - }); - } - if (!(this.element instanceof HTMLCanvasElement)) { - throw new Error('Invalid element'); - } - const payload = this.viewValue; - if (Array.isArray(payload.options) && 0 === payload.options.length) { - payload.options = {}; - } - this.dispatchEvent('pre-connect', { - options: payload.options, - config: payload, - }); - const canvasContext = this.element.getContext('2d'); - if (!canvasContext) { - throw new Error('Could not getContext() from Element'); - } - this.chart = new Chart(canvasContext, payload); - this.dispatchEvent('connect', { chart: this.chart }); + if (!(this.element instanceof HTMLCanvasElement)) { + throw new Error("Invalid element"); } - disconnect() { - this.dispatchEvent('disconnect', { chart: this.chart }); - if (this.chart) { - this.chart.destroy(); - this.chart = null; - } + const payload = this.viewValue; + if (Array.isArray(payload.options) && 0 === payload.options.length) { + payload.options = {}; } - viewValueChanged() { - if (this.chart) { - const viewValue = { data: this.viewValue.data, options: this.viewValue.options }; - if (Array.isArray(viewValue.options) && 0 === viewValue.options.length) { - viewValue.options = {}; - } - this.dispatchEvent('view-value-change', viewValue); - this.chart.data = viewValue.data; - this.chart.options = viewValue.options; - this.chart.update(); - const parentElement = this.element.parentElement; - if (parentElement && this.chart.options.responsive) { - const originalWidth = parentElement.style.width; - parentElement.style.width = `${parentElement.offsetWidth + 1}px`; - setTimeout(() => { - parentElement.style.width = originalWidth; - }, 0); - } - } + this.dispatchEvent("pre-connect", { + options: payload.options, + config: payload + }); + const canvasContext = this.element.getContext("2d"); + if (!canvasContext) { + throw new Error("Could not getContext() from Element"); } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'chartjs' }); + this.chart = new Chart(canvasContext, payload); + this.dispatchEvent("connect", { chart: this.chart }); + } + disconnect() { + this.dispatchEvent("disconnect", { chart: this.chart }); + if (this.chart) { + this.chart.destroy(); + this.chart = null; } -} -default_1.values = { - view: Object, + } + /** + * If the underlying data or options change, let's update the chart! + */ + viewValueChanged() { + if (this.chart) { + const viewValue = { data: this.viewValue.data, options: this.viewValue.options }; + if (Array.isArray(viewValue.options) && 0 === viewValue.options.length) { + viewValue.options = {}; + } + this.dispatchEvent("view-value-change", viewValue); + this.chart.data = viewValue.data; + this.chart.options = viewValue.options; + this.chart.update(); + const parentElement = this.element.parentElement; + if (parentElement && this.chart.options.responsive) { + const originalWidth = parentElement.style.width; + parentElement.style.width = `${parentElement.offsetWidth + 1}px`; + setTimeout(() => { + parentElement.style.width = originalWidth; + }, 0); + } + } + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "chartjs" }); + } +}; +controller_default.values = { + view: Object +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/Cropperjs/assets/dist/controller.d.ts b/src/Cropperjs/assets/dist/controller.d.ts index e8240931bb7..b57528da917 100644 --- a/src/Cropperjs/assets/dist/controller.d.ts +++ b/src/Cropperjs/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class CropperController extends Controller { + +declare class CropperController extends Controller { readonly publicUrlValue: string; readonly optionsValue: object; static values: { @@ -9,3 +10,5 @@ export default class CropperController extends Controller { connect(): void; private dispatchEvent; } + +export { CropperController as default }; diff --git a/src/Cropperjs/assets/dist/controller.js b/src/Cropperjs/assets/dist/controller.js index 2666bf763d5..61c5c427db9 100644 --- a/src/Cropperjs/assets/dist/controller.js +++ b/src/Cropperjs/assets/dist/controller.js @@ -1,31 +1,32 @@ -import { Controller } from '@hotwired/stimulus'; -import Cropper from 'cropperjs'; - -class CropperController extends Controller { - connect() { - const img = document.createElement('img'); - img.classList.add('cropperjs-image'); - img.src = this.publicUrlValue; - const parent = this.element.parentNode; - if (!parent) { - throw new Error('Missing parent node for Cropperjs'); - } - parent.appendChild(img); - const options = this.optionsValue; - this.dispatchEvent('pre-connect', { options, img }); - const cropper = new Cropper(img, options); - img.addEventListener('crop', (event) => { - this.element.value = JSON.stringify(event.detail); - }); - this.dispatchEvent('connect', { cropper, options, img }); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +import Cropper from "cropperjs"; +var CropperController = class extends Controller { + connect() { + const img = document.createElement("img"); + img.classList.add("cropperjs-image"); + img.src = this.publicUrlValue; + const parent = this.element.parentNode; + if (!parent) { + throw new Error("Missing parent node for Cropperjs"); } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'cropperjs' }); - } -} + parent.appendChild(img); + const options = this.optionsValue; + this.dispatchEvent("pre-connect", { options, img }); + const cropper = new Cropper(img, options); + img.addEventListener("crop", (event) => { + this.element.value = JSON.stringify(event.detail); + }); + this.dispatchEvent("connect", { cropper, options, img }); + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "cropperjs" }); + } +}; CropperController.values = { - publicUrl: String, - options: Object, + publicUrl: String, + options: Object +}; +export { + CropperController as default }; - -export { CropperController as default }; diff --git a/src/Dropzone/assets/dist/controller.d.ts b/src/Dropzone/assets/dist/controller.d.ts index 6e67b85b5cd..075cf96c110 100644 --- a/src/Dropzone/assets/dist/controller.d.ts +++ b/src/Dropzone/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { readonly inputTarget: HTMLInputElement; readonly placeholderTarget: HTMLDivElement; readonly previewTarget: HTMLDivElement; @@ -17,3 +18,5 @@ export default class extends Controller { onDragLeave(event: any): void; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/Dropzone/assets/dist/controller.js b/src/Dropzone/assets/dist/controller.js index ebfb380d12a..e08d8371e7e 100644 --- a/src/Dropzone/assets/dist/controller.js +++ b/src/Dropzone/assets/dist/controller.js @@ -1,79 +1,80 @@ -import { Controller } from '@hotwired/stimulus'; - -class default_1 extends Controller { - initialize() { - this.clear = this.clear.bind(this); - this.onInputChange = this.onInputChange.bind(this); - this.onDragEnter = this.onDragEnter.bind(this); - this.onDragLeave = this.onDragLeave.bind(this); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +var controller_default = class extends Controller { + initialize() { + this.clear = this.clear.bind(this); + this.onInputChange = this.onInputChange.bind(this); + this.onDragEnter = this.onDragEnter.bind(this); + this.onDragLeave = this.onDragLeave.bind(this); + } + connect() { + this.clear(); + this.previewClearButtonTarget.addEventListener("click", this.clear); + this.inputTarget.addEventListener("change", this.onInputChange); + this.element.addEventListener("dragenter", this.onDragEnter); + this.element.addEventListener("dragleave", this.onDragLeave); + this.dispatchEvent("connect"); + } + disconnect() { + this.previewClearButtonTarget.removeEventListener("click", this.clear); + this.inputTarget.removeEventListener("change", this.onInputChange); + this.element.removeEventListener("dragenter", this.onDragEnter); + this.element.removeEventListener("dragleave", this.onDragLeave); + } + clear() { + this.inputTarget.value = ""; + this.inputTarget.style.display = "block"; + this.placeholderTarget.style.display = "block"; + this.previewTarget.style.display = "none"; + this.previewImageTarget.style.display = "none"; + this.previewImageTarget.style.backgroundImage = "none"; + this.previewFilenameTarget.textContent = ""; + this.dispatchEvent("clear"); + } + onInputChange(event) { + const file = event.target.files[0]; + if (typeof file === "undefined") { + return; } - connect() { - this.clear(); - this.previewClearButtonTarget.addEventListener('click', this.clear); - this.inputTarget.addEventListener('change', this.onInputChange); - this.element.addEventListener('dragenter', this.onDragEnter); - this.element.addEventListener('dragleave', this.onDragLeave); - this.dispatchEvent('connect'); + this.inputTarget.style.display = "none"; + this.placeholderTarget.style.display = "none"; + this.previewFilenameTarget.textContent = file.name; + this.previewTarget.style.display = "flex"; + this.previewImageTarget.style.display = "none"; + if (file.type && file.type.indexOf("image") !== -1) { + this._populateImagePreview(file); } - disconnect() { - this.previewClearButtonTarget.removeEventListener('click', this.clear); - this.inputTarget.removeEventListener('change', this.onInputChange); - this.element.removeEventListener('dragenter', this.onDragEnter); - this.element.removeEventListener('dragleave', this.onDragLeave); + this.dispatchEvent("change", file); + } + _populateImagePreview(file) { + if (typeof FileReader === "undefined") { + return; } - clear() { - this.inputTarget.value = ''; - this.inputTarget.style.display = 'block'; - this.placeholderTarget.style.display = 'block'; - this.previewTarget.style.display = 'none'; - this.previewImageTarget.style.display = 'none'; - this.previewImageTarget.style.backgroundImage = 'none'; - this.previewFilenameTarget.textContent = ''; - this.dispatchEvent('clear'); + const reader = new FileReader(); + reader.addEventListener("load", (event) => { + this.previewImageTarget.style.display = "block"; + this.previewImageTarget.style.backgroundImage = `url("${event.target.result}")`; + }); + reader.readAsDataURL(file); + } + onDragEnter() { + this.inputTarget.style.display = "block"; + this.placeholderTarget.style.display = "block"; + this.previewTarget.style.display = "none"; + } + onDragLeave(event) { + event.preventDefault(); + if (!this.element.contains(event.relatedTarget)) { + this.inputTarget.style.display = "none"; + this.placeholderTarget.style.display = "none"; + this.previewTarget.style.display = "block"; } - onInputChange(event) { - const file = event.target.files[0]; - if (typeof file === 'undefined') { - return; - } - this.inputTarget.style.display = 'none'; - this.placeholderTarget.style.display = 'none'; - this.previewFilenameTarget.textContent = file.name; - this.previewTarget.style.display = 'flex'; - this.previewImageTarget.style.display = 'none'; - if (file.type && file.type.indexOf('image') !== -1) { - this._populateImagePreview(file); - } - this.dispatchEvent('change', file); - } - _populateImagePreview(file) { - if (typeof FileReader === 'undefined') { - return; - } - const reader = new FileReader(); - reader.addEventListener('load', (event) => { - this.previewImageTarget.style.display = 'block'; - this.previewImageTarget.style.backgroundImage = `url("${event.target.result}")`; - }); - reader.readAsDataURL(file); - } - onDragEnter() { - this.inputTarget.style.display = 'block'; - this.placeholderTarget.style.display = 'block'; - this.previewTarget.style.display = 'none'; - } - onDragLeave(event) { - event.preventDefault(); - if (!this.element.contains(event.relatedTarget)) { - this.inputTarget.style.display = 'none'; - this.placeholderTarget.style.display = 'none'; - this.previewTarget.style.display = 'block'; - } - } - dispatchEvent(name, payload = {}) { - this.dispatch(name, { detail: payload, prefix: 'dropzone' }); - } -} -default_1.targets = ['input', 'placeholder', 'preview', 'previewClearButton', 'previewFilename', 'previewImage']; - -export { default_1 as default }; + } + dispatchEvent(name, payload = {}) { + this.dispatch(name, { detail: payload, prefix: "dropzone" }); + } +}; +controller_default.targets = ["input", "placeholder", "preview", "previewClearButton", "previewFilename", "previewImage"]; +export { + controller_default as default +}; diff --git a/src/LazyImage/assets/dist/controller.d.ts b/src/LazyImage/assets/dist/controller.d.ts index 1ae1c9b7933..d9d7c804eb6 100644 --- a/src/LazyImage/assets/dist/controller.d.ts +++ b/src/LazyImage/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { readonly srcValue: string; readonly srcsetValue: any; readonly hasSrcsetValue: boolean; @@ -11,3 +12,5 @@ export default class extends Controller { _calculateSrcsetString(): string; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/LazyImage/assets/dist/controller.js b/src/LazyImage/assets/dist/controller.js index eb254af1cec..c1cabdcd0a8 100644 --- a/src/LazyImage/assets/dist/controller.js +++ b/src/LazyImage/assets/dist/controller.js @@ -1,39 +1,40 @@ -import { Controller } from '@hotwired/stimulus'; - -class default_1 extends Controller { - connect() { - const hd = new Image(); - const element = this.element; - const srcsetString = this._calculateSrcsetString(); - hd.addEventListener('load', () => { - element.src = this.srcValue; - if (srcsetString) { - element.srcset = srcsetString; - } - this.dispatchEvent('ready', { image: hd }); - }); - hd.src = this.srcValue; - if (srcsetString) { - hd.srcset = srcsetString; - } - this.dispatchEvent('connect', { image: hd }); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +var controller_default = class extends Controller { + connect() { + const hd = new Image(); + const element = this.element; + const srcsetString = this._calculateSrcsetString(); + hd.addEventListener("load", () => { + element.src = this.srcValue; + if (srcsetString) { + element.srcset = srcsetString; + } + this.dispatchEvent("ready", { image: hd }); + }); + hd.src = this.srcValue; + if (srcsetString) { + hd.srcset = srcsetString; } - _calculateSrcsetString() { - if (!this.hasSrcsetValue) { - return ''; - } - const sets = Object.keys(this.srcsetValue).map((size) => { - return `${this.srcsetValue[size]} ${size}`; - }); - return sets.join(', ').trimEnd(); + this.dispatchEvent("connect", { image: hd }); + } + _calculateSrcsetString() { + if (!this.hasSrcsetValue) { + return ""; } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'lazy-image' }); - } -} -default_1.values = { - src: String, - srcset: Object, + const sets = Object.keys(this.srcsetValue).map((size) => { + return `${this.srcsetValue[size]} ${size}`; + }); + return sets.join(", ").trimEnd(); + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "lazy-image" }); + } +}; +controller_default.values = { + src: String, + srcset: Object +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/LiveComponent/assets/dist/Backend/Backend.d.ts b/src/LiveComponent/assets/dist/Backend/Backend.d.ts deleted file mode 100644 index bf93480875c..00000000000 --- a/src/LiveComponent/assets/dist/Backend/Backend.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import BackendRequest from './BackendRequest'; -export interface ChildrenFingerprints { - [key: string]: { - fingerprint: string; - tag: string; - }; -} -export interface BackendInterface { - makeRequest(props: any, actions: BackendAction[], updated: { - [key: string]: any; - }, children: ChildrenFingerprints, updatedPropsFromParent: { - [key: string]: any; - }, files: { - [key: string]: FileList; - }): BackendRequest; -} -export interface BackendAction { - name: string; - args: Record; -} -export default class implements BackendInterface { - private readonly requestBuilder; - constructor(url: string, method?: 'get' | 'post'); - makeRequest(props: any, actions: BackendAction[], updated: { - [key: string]: any; - }, children: ChildrenFingerprints, updatedPropsFromParent: { - [key: string]: any; - }, files: { - [key: string]: FileList; - }): BackendRequest; -} diff --git a/src/LiveComponent/assets/dist/Backend/BackendRequest.d.ts b/src/LiveComponent/assets/dist/Backend/BackendRequest.d.ts deleted file mode 100644 index 9aa7aba20c0..00000000000 --- a/src/LiveComponent/assets/dist/Backend/BackendRequest.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default class { - promise: Promise; - actions: string[]; - updatedModels: string[]; - isResolved: boolean; - constructor(promise: Promise, actions: string[], updateModels: string[]); - containsOneOfActions(targetedActions: string[]): boolean; - areAnyModelsUpdated(targetedModels: string[]): boolean; -} diff --git a/src/LiveComponent/assets/dist/Backend/BackendResponse.d.ts b/src/LiveComponent/assets/dist/Backend/BackendResponse.d.ts deleted file mode 100644 index 71a1b4c0bab..00000000000 --- a/src/LiveComponent/assets/dist/Backend/BackendResponse.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default class { - response: Response; - private body; - private liveUrl; - constructor(response: Response); - getBody(): Promise; - getLiveUrl(): string | null; -} diff --git a/src/LiveComponent/assets/dist/Backend/RequestBuilder.d.ts b/src/LiveComponent/assets/dist/Backend/RequestBuilder.d.ts deleted file mode 100644 index 45821c56b5f..00000000000 --- a/src/LiveComponent/assets/dist/Backend/RequestBuilder.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { BackendAction, ChildrenFingerprints } from './Backend'; -export default class { - private url; - private method; - constructor(url: string, method?: 'get' | 'post'); - buildRequest(props: any, actions: BackendAction[], updated: { - [key: string]: any; - }, children: ChildrenFingerprints, updatedPropsFromParent: { - [key: string]: any; - }, files: { - [key: string]: FileList; - }): { - url: string; - fetchOptions: RequestInit; - }; - private willDataFitInUrl; -} diff --git a/src/LiveComponent/assets/dist/Component/ElementDriver.d.ts b/src/LiveComponent/assets/dist/Component/ElementDriver.d.ts deleted file mode 100644 index 40839e8e21a..00000000000 --- a/src/LiveComponent/assets/dist/Component/ElementDriver.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type LiveControllerDefault from '../live_controller'; -export interface ElementDriver { - getModelName(element: HTMLElement): string | null; - getComponentProps(): any; - getEventsToEmit(): Array<{ - event: string; - data: any; - target: string | null; - componentName: string | null; - }>; - getBrowserEventsToDispatch(): Array<{ - event: string; - payload: any; - }>; -} -export declare class StimulusElementDriver implements ElementDriver { - private readonly controller; - constructor(controller: LiveControllerDefault); - getModelName(element: HTMLElement): string | null; - getComponentProps(): any; - getEventsToEmit(): Array<{ - event: string; - data: any; - target: string | null; - componentName: string | null; - }>; - getBrowserEventsToDispatch(): Array<{ - event: string; - payload: any; - }>; -} diff --git a/src/LiveComponent/assets/dist/Component/UnsyncedInputsTracker.d.ts b/src/LiveComponent/assets/dist/Component/UnsyncedInputsTracker.d.ts deleted file mode 100644 index d36c62b7810..00000000000 --- a/src/LiveComponent/assets/dist/Component/UnsyncedInputsTracker.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ElementDriver } from './ElementDriver'; -import type Component from './index'; -export default class { - private readonly component; - private readonly modelElementResolver; - private readonly unsyncedInputs; - private elementEventListeners; - constructor(component: Component, modelElementResolver: ElementDriver); - activate(): void; - deactivate(): void; - markModelAsSynced(modelName: string): void; - private handleInputEvent; - private updateModelFromElement; - getUnsyncedInputs(): HTMLElement[]; - getUnsyncedModels(): string[]; - resetUnsyncedFields(): void; -} -export declare class UnsyncedInputContainer { - private unsyncedModelFields; - private unsyncedNonModelFields; - private unsyncedModelNames; - constructor(); - add(element: HTMLElement, modelName?: string | null): void; - resetUnsyncedFields(): void; - allUnsyncedInputs(): HTMLElement[]; - markModelAsSynced(modelName: string): void; - getUnsyncedModelNames(): string[]; -} diff --git a/src/LiveComponent/assets/dist/Component/ValueStore.d.ts b/src/LiveComponent/assets/dist/Component/ValueStore.d.ts deleted file mode 100644 index a0eb9ca8346..00000000000 --- a/src/LiveComponent/assets/dist/Component/ValueStore.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export default class { - private props; - private dirtyProps; - private pendingProps; - private updatedPropsFromParent; - constructor(props: any); - get(name: string): any; - has(name: string): boolean; - set(name: string, value: any): boolean; - getOriginalProps(): any; - getDirtyProps(): any; - getUpdatedPropsFromParent(): any; - flushDirtyPropsToPending(): void; - reinitializeAllProps(props: any): void; - pushPendingPropsBackToDirty(): void; - storeNewPropsFromParent(props: any): boolean; -} diff --git a/src/LiveComponent/assets/dist/Component/index.d.ts b/src/LiveComponent/assets/dist/Component/index.d.ts deleted file mode 100644 index 65a18a486db..00000000000 --- a/src/LiveComponent/assets/dist/Component/index.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { BackendInterface } from '../Backend/Backend'; -import type BackendRequest from '../Backend/BackendRequest'; -import BackendResponse from '../Backend/BackendResponse'; -import type { ElementDriver } from './ElementDriver'; -import type { PluginInterface } from './plugins/PluginInterface'; -import ValueStore from './ValueStore'; -type MaybePromise = T | Promise; -export type ComponentHooks = { - connect: (component: Component) => MaybePromise; - disconnect: (component: Component) => MaybePromise; - 'request:started': (requestConfig: any) => MaybePromise; - 'render:finished': (component: Component) => MaybePromise; - 'response:error': (backendResponse: BackendResponse, controls: { - displayError: boolean; - }) => MaybePromise; - 'loading.state:started': (element: HTMLElement, request: BackendRequest) => MaybePromise; - 'loading.state:finished': (element: HTMLElement) => MaybePromise; - 'model:set': (model: string, value: any, component: Component) => MaybePromise; -}; -export type ComponentHookName = keyof ComponentHooks; -export type ComponentHookCallback = T extends ComponentHookName ? ComponentHooks[T] : (...args: any[]) => MaybePromise; -export default class Component { - readonly element: HTMLElement; - readonly name: string; - readonly listeners: Map; - private backend; - readonly elementDriver: ElementDriver; - id: string | null; - fingerprint: string; - readonly valueStore: ValueStore; - private readonly unsyncedInputsTracker; - private hooks; - defaultDebounce: number; - private backendRequest; - private pendingActions; - private pendingFiles; - private isRequestPending; - private requestDebounceTimeout; - private nextRequestPromise; - private nextRequestPromiseResolve; - private externalMutationTracker; - constructor(element: HTMLElement, name: string, props: any, listeners: Array<{ - event: string; - action: string; - }>, id: string | null, backend: BackendInterface, elementDriver: ElementDriver); - addPlugin(plugin: PluginInterface): void; - connect(): void; - disconnect(): void; - on(hookName: T, callback: ComponentHookCallback): void; - off(hookName: T, callback: ComponentHookCallback): void; - set(model: string, value: any, reRender?: boolean, debounce?: number | boolean): Promise; - getData(model: string): any; - action(name: string, args?: any, debounce?: number | boolean): Promise; - files(key: string, input: HTMLInputElement): void; - render(): Promise; - getUnsyncedModels(): string[]; - emit(name: string, data: any, onlyMatchingComponentsNamed?: string | null): void; - emitUp(name: string, data: any, onlyMatchingComponentsNamed?: string | null): void; - emitSelf(name: string, data: any): void; - private performEmit; - private doEmit; - private isTurboEnabled; - private tryStartingRequest; - private performRequest; - private processRerender; - private calculateDebounce; - private clearRequestDebounceTimeout; - private debouncedStartRequest; - private renderError; - private resetPromise; - _updateFromParentProps(props: any): void; -} -export declare function proxifyComponent(component: Component): Component; -export {}; diff --git a/src/LiveComponent/assets/dist/Component/plugins/ChildComponentPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/ChildComponentPlugin.d.ts deleted file mode 100644 index 9003b387850..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/ChildComponentPlugin.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type Component from '../../Component'; -import type { PluginInterface } from './PluginInterface'; -export default class implements PluginInterface { - private readonly component; - private parentModelBindings; - constructor(component: Component); - attachToComponent(component: Component): void; - private getChildrenFingerprints; - private notifyParentModelChange; - private getChildren; -} diff --git a/src/LiveComponent/assets/dist/Component/plugins/LazyPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/LazyPlugin.d.ts deleted file mode 100644 index a69a080ae83..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/LazyPlugin.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type Component from '../index'; -import type { PluginInterface } from './PluginInterface'; -export default class implements PluginInterface { - private intersectionObserver; - attachToComponent(component: Component): void; - private getObserver; -} diff --git a/src/LiveComponent/assets/dist/Component/plugins/LoadingPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/LoadingPlugin.d.ts deleted file mode 100644 index 0ecb4a3bad8..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/LoadingPlugin.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type BackendRequest from '../../Backend/BackendRequest'; -import type Component from '../../Component'; -import { type Directive } from '../../Directive/directives_parser'; -import type { PluginInterface } from './PluginInterface'; -interface ElementLoadingDirectives { - element: HTMLElement | SVGElement; - directives: Directive[]; -} -export default class implements PluginInterface { - attachToComponent(component: Component): void; - startLoading(component: Component, targetElement: HTMLElement | SVGElement, backendRequest: BackendRequest): void; - finishLoading(component: Component, targetElement: HTMLElement | SVGElement): void; - private handleLoadingToggle; - private handleLoadingDirective; - getLoadingDirectives(component: Component, element: HTMLElement | SVGElement): ElementLoadingDirectives[]; - private showElement; - private hideElement; - private addClass; - private removeClass; - private addAttributes; - private removeAttributes; -} -export {}; diff --git a/src/LiveComponent/assets/dist/Component/plugins/PageUnloadingPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/PageUnloadingPlugin.d.ts deleted file mode 100644 index 8af34c4c522..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/PageUnloadingPlugin.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type Component from '../index'; -import type { PluginInterface } from './PluginInterface'; -export default class implements PluginInterface { - private isConnected; - attachToComponent(component: Component): void; -} diff --git a/src/LiveComponent/assets/dist/Component/plugins/PluginInterface.d.ts b/src/LiveComponent/assets/dist/Component/plugins/PluginInterface.d.ts deleted file mode 100644 index e142dcadf54..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/PluginInterface.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type Component from '../index'; -export interface PluginInterface { - attachToComponent(component: Component): void; -} diff --git a/src/LiveComponent/assets/dist/Component/plugins/PollingPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/PollingPlugin.d.ts deleted file mode 100644 index 71c09d4d3b6..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/PollingPlugin.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type Component from '../index'; -import type { PluginInterface } from './PluginInterface'; -export default class implements PluginInterface { - private element; - private pollingDirector; - attachToComponent(component: Component): void; - addPoll(actionName: string, duration: number): void; - clearPolling(): void; - private initializePolling; -} diff --git a/src/LiveComponent/assets/dist/Component/plugins/SetValueOntoModelFieldsPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/SetValueOntoModelFieldsPlugin.d.ts deleted file mode 100644 index eecde830175..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/SetValueOntoModelFieldsPlugin.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type Component from '../index'; -import type { PluginInterface } from './PluginInterface'; -export default class implements PluginInterface { - attachToComponent(component: Component): void; - private synchronizeValueOfModelFields; -} diff --git a/src/LiveComponent/assets/dist/Component/plugins/ValidatedFieldsPlugin.d.ts b/src/LiveComponent/assets/dist/Component/plugins/ValidatedFieldsPlugin.d.ts deleted file mode 100644 index ef285fea028..00000000000 --- a/src/LiveComponent/assets/dist/Component/plugins/ValidatedFieldsPlugin.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type Component from '../index'; -import type { PluginInterface } from './PluginInterface'; -export default class implements PluginInterface { - attachToComponent(component: Component): void; - private handleModelSet; -} diff --git a/src/LiveComponent/assets/dist/ComponentRegistry.d.ts b/src/LiveComponent/assets/dist/ComponentRegistry.d.ts deleted file mode 100644 index 14aba325b51..00000000000 --- a/src/LiveComponent/assets/dist/ComponentRegistry.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type Component from './Component'; -export declare const resetRegistry: () => void; -export declare const registerComponent: (component: Component) => void; -export declare const unregisterComponent: (component: Component) => void; -export declare const getComponent: (element: HTMLElement) => Promise; -export declare const findComponents: (currentComponent: Component, onlyParents: boolean, onlyMatchName: string | null) => Component[]; -export declare const findChildren: (currentComponent: Component) => Component[]; -export declare const findParent: (currentComponent: Component) => Component | null; diff --git a/src/LiveComponent/assets/dist/Directive/directives_parser.d.ts b/src/LiveComponent/assets/dist/Directive/directives_parser.d.ts deleted file mode 100644 index 70ab5eef5d9..00000000000 --- a/src/LiveComponent/assets/dist/Directive/directives_parser.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface DirectiveModifier { - name: string; - value: string | null; -} -export interface Directive { - action: string; - args: string[]; - modifiers: DirectiveModifier[]; - getString: () => string; -} -export declare function parseDirectives(content: string | null): Directive[]; diff --git a/src/LiveComponent/assets/dist/Directive/get_model_binding.d.ts b/src/LiveComponent/assets/dist/Directive/get_model_binding.d.ts deleted file mode 100644 index daaeb36f913..00000000000 --- a/src/LiveComponent/assets/dist/Directive/get_model_binding.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Directive } from './directives_parser'; -export interface ModelBinding { - modelName: string; - innerModelName: string | null; - shouldRender: boolean; - debounce: number | boolean; - targetEventName: string | null; - minLength: number | null; - maxLength: number | null; - minValue: number | null; - maxValue: number | null; -} -export default function (modelDirective: Directive): ModelBinding; diff --git a/src/LiveComponent/assets/dist/HookManager.d.ts b/src/LiveComponent/assets/dist/HookManager.d.ts deleted file mode 100644 index 51ad961a9b5..00000000000 --- a/src/LiveComponent/assets/dist/HookManager.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ComponentHookCallback, ComponentHookName } from './Component'; -export default class { - private hooks; - register(hookName: T, callback: ComponentHookCallback): void; - unregister(hookName: T, callback: ComponentHookCallback): void; - triggerHook(hookName: T, ...args: Parameters>): void; -} diff --git a/src/LiveComponent/assets/dist/PollingDirector.d.ts b/src/LiveComponent/assets/dist/PollingDirector.d.ts deleted file mode 100644 index 52d7136e006..00000000000 --- a/src/LiveComponent/assets/dist/PollingDirector.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type Component from './Component'; -export default class { - component: Component; - isPollingActive: boolean; - polls: Array<{ - actionName: string; - duration: number; - }>; - pollingIntervals: number[]; - constructor(component: Component); - addPoll(actionName: string, duration: number): void; - startAllPolling(): void; - stopAllPolling(): void; - clearPolling(): void; - private initiatePoll; -} diff --git a/src/LiveComponent/assets/dist/Rendering/ChangingItemsTracker.d.ts b/src/LiveComponent/assets/dist/Rendering/ChangingItemsTracker.d.ts deleted file mode 100644 index e1c5684a229..00000000000 --- a/src/LiveComponent/assets/dist/Rendering/ChangingItemsTracker.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export default class { - private changedItems; - private removedItems; - setItem(itemName: string, newValue: string, previousValue: string | null): void; - removeItem(itemName: string, currentValue: string | null): void; - getChangedItems(): { - name: string; - value: string; - }[]; - getRemovedItems(): string[]; - isEmpty(): boolean; -} diff --git a/src/LiveComponent/assets/dist/Rendering/ElementChanges.d.ts b/src/LiveComponent/assets/dist/Rendering/ElementChanges.d.ts deleted file mode 100644 index 8d695ed905e..00000000000 --- a/src/LiveComponent/assets/dist/Rendering/ElementChanges.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -export default class ElementChanges { - private addedClasses; - private removedClasses; - private styleChanges; - private attributeChanges; - addClass(className: string): void; - removeClass(className: string): void; - addStyle(styleName: string, newValue: string, originalValue: string | null): void; - removeStyle(styleName: string, originalValue: string): void; - addAttribute(attributeName: string, newValue: string, originalValue: string | null): void; - removeAttribute(attributeName: string, originalValue: string | null): void; - getAddedClasses(): string[]; - getRemovedClasses(): string[]; - getChangedStyles(): { - name: string; - value: string; - }[]; - getRemovedStyles(): string[]; - getChangedAttributes(): { - name: string; - value: string; - }[]; - getRemovedAttributes(): string[]; - applyToElement(element: HTMLElement): void; - isEmpty(): boolean; -} diff --git a/src/LiveComponent/assets/dist/Rendering/ExternalMutationTracker.d.ts b/src/LiveComponent/assets/dist/Rendering/ExternalMutationTracker.d.ts deleted file mode 100644 index 0d76f2c22eb..00000000000 --- a/src/LiveComponent/assets/dist/Rendering/ExternalMutationTracker.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -import ElementChanges from './ElementChanges'; -export default class { - private element; - private shouldTrackChangeCallback; - private mutationObserver; - private changedElements; - changedElementsCount: number; - private addedElements; - private removedElements; - private isStarted; - constructor(element: Element, shouldTrackChangeCallback: (element: Element) => boolean); - start(): void; - stop(): void; - getChangedElement(element: Element): ElementChanges | null; - getAddedElements(): Element[]; - wasElementAdded(element: Element): boolean; - handlePendingChanges(): void; - private onMutations; - private handleChildListMutation; - private handleAttributeMutation; - private handleClassAttributeMutation; - private handleStyleAttributeMutation; - private handleGenericAttributeMutation; - private extractStyles; - private isElementAddedByTranslation; -} diff --git a/src/LiveComponent/assets/dist/Util/getElementAsTagText.d.ts b/src/LiveComponent/assets/dist/Util/getElementAsTagText.d.ts deleted file mode 100644 index dbd343f3e80..00000000000 --- a/src/LiveComponent/assets/dist/Util/getElementAsTagText.d.ts +++ /dev/null @@ -1 +0,0 @@ -export default function getElementAsTagText(element: HTMLElement): string; diff --git a/src/LiveComponent/assets/dist/data_manipulation_utils.d.ts b/src/LiveComponent/assets/dist/data_manipulation_utils.d.ts deleted file mode 100644 index fd8fa503fa5..00000000000 --- a/src/LiveComponent/assets/dist/data_manipulation_utils.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare function getDeepData(data: any, propertyPath: string): any; -export declare function setDeepData(data: any, propertyPath: string, value: any): any; diff --git a/src/LiveComponent/assets/dist/dom_utils.d.ts b/src/LiveComponent/assets/dist/dom_utils.d.ts deleted file mode 100644 index af330333c4e..00000000000 --- a/src/LiveComponent/assets/dist/dom_utils.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type Component from './Component'; -import type ValueStore from './Component/ValueStore'; -import { type Directive } from './Directive/directives_parser'; -export declare function getValueFromElement(element: HTMLElement, valueStore: ValueStore): string | string[] | null | boolean; -export declare function setValueOnElement(element: HTMLElement, value: any): void; -export declare function getAllModelDirectiveFromElements(element: HTMLElement): Directive[]; -export declare function getModelDirectiveFromElement(element: HTMLElement, throwOnMissing?: boolean): null | Directive; -export declare function elementBelongsToThisComponent(element: Element, component: Component): boolean; -export declare function cloneHTMLElement(element: HTMLElement): HTMLElement; -export declare function htmlToElement(html: string): HTMLElement; -export declare function isTextualInputElement(el: HTMLElement): el is HTMLInputElement; -export declare function isTextareaElement(el: HTMLElement): el is HTMLTextAreaElement; -export declare function isNumericalInputElement(element: Element): element is HTMLInputElement; diff --git a/src/LiveComponent/assets/dist/live_controller.d.ts b/src/LiveComponent/assets/dist/live_controller.d.ts index 21a6b186ce8..c032c778f57 100644 --- a/src/LiveComponent/assets/dist/live_controller.d.ts +++ b/src/LiveComponent/assets/dist/live_controller.d.ts @@ -1,19 +1,161 @@ import { Controller } from '@hotwired/stimulus'; -import { type BackendInterface } from './Backend/Backend'; -import Component from './Component'; -export { Component }; -export { getComponent } from './ComponentRegistry'; -export interface LiveEvent extends CustomEvent { + +declare class export_default$2{ + response: Response; + private body; + private liveUrl; + constructor(response: Response); + getBody(): Promise; + getLiveUrl(): string | null; +} + +declare class export_default$1{ + promise: Promise; + actions: string[]; + updatedModels: string[]; + isResolved: boolean; + constructor(promise: Promise, actions: string[], updateModels: string[]); + containsOneOfActions(targetedActions: string[]): boolean; + areAnyModelsUpdated(targetedModels: string[]): boolean; +} + +interface ChildrenFingerprints { + [key: string]: { + fingerprint: string; + tag: string; + }; +} +interface BackendInterface { + makeRequest(props: any, actions: BackendAction[], updated: { + [key: string]: any; + }, children: ChildrenFingerprints, updatedPropsFromParent: { + [key: string]: any; + }, files: { + [key: string]: FileList; + }): export_default$1; +} +interface BackendAction { + name: string; + args: Record; +} + +interface ElementDriver { + getModelName(element: HTMLElement): string | null; + getComponentProps(): any; + getEventsToEmit(): Array<{ + event: string; + data: any; + target: string | null; + componentName: string | null; + }>; + getBrowserEventsToDispatch(): Array<{ + event: string; + payload: any; + }>; +} + +interface PluginInterface { + attachToComponent(component: Component): void; +} + +declare class export_default{ + private props; + private dirtyProps; + private pendingProps; + private updatedPropsFromParent; + constructor(props: any); + get(name: string): any; + has(name: string): boolean; + set(name: string, value: any): boolean; + getOriginalProps(): any; + getDirtyProps(): any; + getUpdatedPropsFromParent(): any; + flushDirtyPropsToPending(): void; + reinitializeAllProps(props: any): void; + pushPendingPropsBackToDirty(): void; + storeNewPropsFromParent(props: any): boolean; +} + +type MaybePromise = T | Promise; +type ComponentHooks = { + connect: (component: Component) => MaybePromise; + disconnect: (component: Component) => MaybePromise; + 'request:started': (requestConfig: any) => MaybePromise; + 'render:finished': (component: Component) => MaybePromise; + 'response:error': (backendResponse: export_default$2, controls: { + displayError: boolean; + }) => MaybePromise; + 'loading.state:started': (element: HTMLElement, request: export_default$1) => MaybePromise; + 'loading.state:finished': (element: HTMLElement) => MaybePromise; + 'model:set': (model: string, value: any, component: Component) => MaybePromise; +}; +type ComponentHookName = keyof ComponentHooks; +type ComponentHookCallback = T extends ComponentHookName ? ComponentHooks[T] : (...args: any[]) => MaybePromise; +declare class Component { + readonly element: HTMLElement; + readonly name: string; + readonly listeners: Map; + private backend; + readonly elementDriver: ElementDriver; + id: string | null; + fingerprint: string; + readonly valueStore: export_default; + private readonly unsyncedInputsTracker; + private hooks; + defaultDebounce: number; + private backendRequest; + private pendingActions; + private pendingFiles; + private isRequestPending; + private requestDebounceTimeout; + private nextRequestPromise; + private nextRequestPromiseResolve; + private externalMutationTracker; + constructor(element: HTMLElement, name: string, props: any, listeners: Array<{ + event: string; + action: string; + }>, id: string | null, backend: BackendInterface, elementDriver: ElementDriver); + addPlugin(plugin: PluginInterface): void; + connect(): void; + disconnect(): void; + on(hookName: T, callback: ComponentHookCallback): void; + off(hookName: T, callback: ComponentHookCallback): void; + set(model: string, value: any, reRender?: boolean, debounce?: number | boolean): Promise; + getData(model: string): any; + action(name: string, args?: any, debounce?: number | boolean): Promise; + files(key: string, input: HTMLInputElement): void; + render(): Promise; + getUnsyncedModels(): string[]; + emit(name: string, data: any, onlyMatchingComponentsNamed?: string | null): void; + emitUp(name: string, data: any, onlyMatchingComponentsNamed?: string | null): void; + emitSelf(name: string, data: any): void; + private performEmit; + private doEmit; + private isTurboEnabled; + private tryStartingRequest; + private performRequest; + private processRerender; + private calculateDebounce; + private clearRequestDebounceTimeout; + private debouncedStartRequest; + private renderError; + private resetPromise; + _updateFromParentProps(props: any): void; +} + +declare const getComponent: (element: HTMLElement) => Promise; + +interface LiveEvent extends CustomEvent { detail: { controller: LiveController; component: Component; }; } -export interface LiveController { +interface LiveController { element: HTMLElement; component: Component; } -export default class LiveControllerDefault extends Controller implements LiveController { +declare class LiveControllerDefault extends Controller implements LiveController { static values: { name: StringConstructor; url: StringConstructor; @@ -84,11 +226,11 @@ export default class LiveControllerDefault extends Controller imple disconnect(): void; update(event: any): void; action(event: any): void; - $render(): Promise; + $render(): Promise; emit(event: any): void; emitUp(event: any): void; emitSelf(event: any): void; - $updateModel(model: string, value: any, shouldRender?: boolean, debounce?: number | boolean): Promise; + $updateModel(model: string, value: any, shouldRender?: boolean, debounce?: number | boolean): Promise; propsUpdatedFromParentValueChanged(): void; fingerprintValueChanged(): void; private getEmitDirectives; @@ -101,3 +243,5 @@ export default class LiveControllerDefault extends Controller imple private dispatchEvent; private onMutations; } + +export { Component, type LiveController, type LiveEvent, LiveControllerDefault as default, getComponent }; diff --git a/src/LiveComponent/assets/dist/live_controller.js b/src/LiveComponent/assets/dist/live_controller.js index 96f5062d316..b1f8049a6f4 100644 --- a/src/LiveComponent/assets/dist/live_controller.js +++ b/src/LiveComponent/assets/dist/live_controller.js @@ -1,3126 +1,3089 @@ -import { Controller } from '@hotwired/stimulus'; - -class BackendRequest { - constructor(promise, actions, updateModels) { - this.isResolved = false; - this.promise = promise; - this.promise.then((response) => { - this.isResolved = true; - return response; - }); - this.actions = actions; - this.updatedModels = updateModels; - } - containsOneOfActions(targetedActions) { - return this.actions.filter((action) => targetedActions.includes(action)).length > 0; - } - areAnyModelsUpdated(targetedModels) { - return this.updatedModels.filter((model) => targetedModels.includes(model)).length > 0; - } -} +// src/live_controller.ts +import { Controller } from "@hotwired/stimulus"; + +// src/Backend/BackendRequest.ts +var BackendRequest_default = class { + constructor(promise, actions, updateModels) { + this.isResolved = false; + this.promise = promise; + this.promise.then((response) => { + this.isResolved = true; + return response; + }); + this.actions = actions; + this.updatedModels = updateModels; + } + /** + * Does this BackendRequest contain at least on action in targetedActions? + */ + containsOneOfActions(targetedActions) { + return this.actions.filter((action) => targetedActions.includes(action)).length > 0; + } + /** + * Does this BackendRequest includes updates for any of these models? + */ + areAnyModelsUpdated(targetedModels) { + return this.updatedModels.filter((model) => targetedModels.includes(model)).length > 0; + } +}; -class RequestBuilder { - constructor(url, method = 'post') { - this.url = url; - this.method = method; - } - buildRequest(props, actions, updated, children, updatedPropsFromParent, files) { - const splitUrl = this.url.split('?'); - let [url] = splitUrl; - const [, queryString] = splitUrl; - const params = new URLSearchParams(queryString || ''); - const fetchOptions = {}; - fetchOptions.headers = { - Accept: 'application/vnd.live-component+html', - 'X-Requested-With': 'XMLHttpRequest', - 'X-Live-Url': window.location.pathname + window.location.search, - }; - const totalFiles = Object.entries(files).reduce((total, current) => total + current.length, 0); - const hasFingerprints = Object.keys(children).length > 0; - if (actions.length === 0 && - totalFiles === 0 && - this.method === 'get' && - this.willDataFitInUrl(JSON.stringify(props), JSON.stringify(updated), params, JSON.stringify(children), JSON.stringify(updatedPropsFromParent))) { - params.set('props', JSON.stringify(props)); - params.set('updated', JSON.stringify(updated)); - if (Object.keys(updatedPropsFromParent).length > 0) { - params.set('propsFromParent', JSON.stringify(updatedPropsFromParent)); - } - if (hasFingerprints) { - params.set('children', JSON.stringify(children)); - } - fetchOptions.method = 'GET'; - } - else { - fetchOptions.method = 'POST'; - const requestData = { props, updated }; - if (Object.keys(updatedPropsFromParent).length > 0) { - requestData.propsFromParent = updatedPropsFromParent; - } - if (hasFingerprints) { - requestData.children = children; - } - if (actions.length > 0) { - if (actions.length === 1) { - requestData.args = actions[0].args; - url += `/${encodeURIComponent(actions[0].name)}`; - } - else { - url += '/_batch'; - requestData.actions = actions; - } - } - const formData = new FormData(); - formData.append('data', JSON.stringify(requestData)); - for (const [key, value] of Object.entries(files)) { - const length = value.length; - for (let i = 0; i < length; ++i) { - formData.append(key, value[i]); - } - } - fetchOptions.body = formData; - } - const paramsString = params.toString(); - return { - url: `${url}${paramsString.length > 0 ? `?${paramsString}` : ''}`, - fetchOptions, - }; - } - willDataFitInUrl(propsJson, updatedJson, params, childrenJson, propsFromParentJson) { - const urlEncodedJsonData = new URLSearchParams(propsJson + updatedJson + childrenJson + propsFromParentJson).toString(); - return (urlEncodedJsonData + params.toString()).length < 1500; - } -} +// src/Backend/RequestBuilder.ts +var RequestBuilder_default = class { + constructor(url, method = "post") { + this.url = url; + this.method = method; + } + buildRequest(props, actions, updated, children, updatedPropsFromParent, files) { + const splitUrl = this.url.split("?"); + let [url] = splitUrl; + const [, queryString] = splitUrl; + const params = new URLSearchParams(queryString || ""); + const fetchOptions = {}; + fetchOptions.headers = { + Accept: "application/vnd.live-component+html", + "X-Requested-With": "XMLHttpRequest", + "X-Live-Url": window.location.pathname + window.location.search + }; + const totalFiles = Object.entries(files).reduce((total, current) => total + current.length, 0); + const hasFingerprints = Object.keys(children).length > 0; + if (actions.length === 0 && totalFiles === 0 && this.method === "get" && this.willDataFitInUrl( + JSON.stringify(props), + JSON.stringify(updated), + params, + JSON.stringify(children), + JSON.stringify(updatedPropsFromParent) + )) { + params.set("props", JSON.stringify(props)); + params.set("updated", JSON.stringify(updated)); + if (Object.keys(updatedPropsFromParent).length > 0) { + params.set("propsFromParent", JSON.stringify(updatedPropsFromParent)); + } + if (hasFingerprints) { + params.set("children", JSON.stringify(children)); + } + fetchOptions.method = "GET"; + } else { + fetchOptions.method = "POST"; + const requestData = { props, updated }; + if (Object.keys(updatedPropsFromParent).length > 0) { + requestData.propsFromParent = updatedPropsFromParent; + } + if (hasFingerprints) { + requestData.children = children; + } + if (actions.length > 0) { + if (actions.length === 1) { + requestData.args = actions[0].args; + url += `/${encodeURIComponent(actions[0].name)}`; + } else { + url += "/_batch"; + requestData.actions = actions; + } + } + const formData = new FormData(); + formData.append("data", JSON.stringify(requestData)); + for (const [key, value] of Object.entries(files)) { + const length = value.length; + for (let i = 0; i < length; ++i) { + formData.append(key, value[i]); + } + } + fetchOptions.body = formData; + } + const paramsString = params.toString(); + return { + url: `${url}${paramsString.length > 0 ? `?${paramsString}` : ""}`, + fetchOptions + }; + } + willDataFitInUrl(propsJson, updatedJson, params, childrenJson, propsFromParentJson) { + const urlEncodedJsonData = new URLSearchParams( + propsJson + updatedJson + childrenJson + propsFromParentJson + ).toString(); + return (urlEncodedJsonData + params.toString()).length < 1500; + } +}; -class Backend { - constructor(url, method = 'post') { - this.requestBuilder = new RequestBuilder(url, method); - } - makeRequest(props, actions, updated, children, updatedPropsFromParent, files) { - const { url, fetchOptions } = this.requestBuilder.buildRequest(props, actions, updated, children, updatedPropsFromParent, files); - return new BackendRequest(fetch(url, fetchOptions), actions.map((backendAction) => backendAction.name), Object.keys(updated)); - } -} +// src/Backend/Backend.ts +var Backend_default = class { + constructor(url, method = "post") { + this.requestBuilder = new RequestBuilder_default(url, method); + } + makeRequest(props, actions, updated, children, updatedPropsFromParent, files) { + const { url, fetchOptions } = this.requestBuilder.buildRequest( + props, + actions, + updated, + children, + updatedPropsFromParent, + files + ); + return new BackendRequest_default( + fetch(url, fetchOptions), + actions.map((backendAction) => backendAction.name), + Object.keys(updated) + ); + } +}; -class BackendResponse { - constructor(response) { - this.response = response; - } - async getBody() { - if (!this.body) { - this.body = await this.response.text(); - } - return this.body; - } - getLiveUrl() { - if (undefined === this.liveUrl) { - this.liveUrl = this.response.headers.get('X-Live-Url'); - } - return this.liveUrl; - } -} +// src/Backend/BackendResponse.ts +var BackendResponse_default = class { + constructor(response) { + this.response = response; + } + async getBody() { + if (!this.body) { + this.body = await this.response.text(); + } + return this.body; + } + getLiveUrl() { + if (void 0 === this.liveUrl) { + this.liveUrl = this.response.headers.get("X-Live-Url"); + } + return this.liveUrl; + } +}; +// src/Util/getElementAsTagText.ts function getElementAsTagText(element) { - return element.innerHTML - ? element.outerHTML.slice(0, element.outerHTML.indexOf(element.innerHTML)) - : element.outerHTML; + return element.innerHTML ? element.outerHTML.slice(0, element.outerHTML.indexOf(element.innerHTML)) : element.outerHTML; } -let componentMapByElement = new WeakMap(); -let componentMapByComponent = new Map(); -const registerComponent = (component) => { - componentMapByElement.set(component.element, component); - componentMapByComponent.set(component, component.name); +// src/ComponentRegistry.ts +var componentMapByElement = /* @__PURE__ */ new WeakMap(); +var componentMapByComponent = /* @__PURE__ */ new Map(); +var registerComponent = (component) => { + componentMapByElement.set(component.element, component); + componentMapByComponent.set(component, component.name); }; -const unregisterComponent = (component) => { - componentMapByElement.delete(component.element); - componentMapByComponent.delete(component); +var unregisterComponent = (component) => { + componentMapByElement.delete(component.element); + componentMapByComponent.delete(component); }; -const getComponent = (element) => new Promise((resolve, reject) => { - let count = 0; - const maxCount = 10; - const interval = setInterval(() => { - const component = componentMapByElement.get(element); - if (component) { - clearInterval(interval); - resolve(component); - } - count++; - if (count > maxCount) { - clearInterval(interval); - reject(new Error(`Component not found for element ${getElementAsTagText(element)}`)); - } - }, 5); +var getComponent = (element) => new Promise((resolve, reject) => { + let count = 0; + const maxCount = 10; + const interval = setInterval(() => { + const component = componentMapByElement.get(element); + if (component) { + clearInterval(interval); + resolve(component); + } + count++; + if (count > maxCount) { + clearInterval(interval); + reject(new Error(`Component not found for element ${getElementAsTagText(element)}`)); + } + }, 5); }); -const findComponents = (currentComponent, onlyParents, onlyMatchName) => { - const components = []; - componentMapByComponent.forEach((componentName, component) => { - if (onlyParents && (currentComponent === component || !component.element.contains(currentComponent.element))) { - return; - } - if (onlyMatchName && componentName !== onlyMatchName) { - return; - } - components.push(component); - }); - return components; +var findComponents = (currentComponent, onlyParents, onlyMatchName) => { + const components = []; + componentMapByComponent.forEach((componentName, component) => { + if (onlyParents && (currentComponent === component || !component.element.contains(currentComponent.element))) { + return; + } + if (onlyMatchName && componentName !== onlyMatchName) { + return; + } + components.push(component); + }); + return components; }; -const findChildren = (currentComponent) => { - const children = []; - componentMapByComponent.forEach((componentName, component) => { - if (currentComponent === component) { - return; - } - if (!currentComponent.element.contains(component.element)) { - return; - } - let foundChildComponent = false; - componentMapByComponent.forEach((childComponentName, childComponent) => { - if (foundChildComponent) { - return; - } - if (childComponent === component) { - return; - } - if (childComponent.element.contains(component.element)) { - foundChildComponent = true; - } - }); - children.push(component); +var findChildren = (currentComponent) => { + const children = []; + componentMapByComponent.forEach((componentName, component) => { + if (currentComponent === component) { + return; + } + if (!currentComponent.element.contains(component.element)) { + return; + } + let foundChildComponent = false; + componentMapByComponent.forEach((childComponentName, childComponent) => { + if (foundChildComponent) { + return; + } + if (childComponent === component) { + return; + } + if (childComponent.element.contains(component.element)) { + foundChildComponent = true; + } }); - return children; + children.push(component); + }); + return children; }; -const findParent = (currentComponent) => { - let parentElement = currentComponent.element.parentElement; - while (parentElement) { - const component = componentMapByElement.get(parentElement); - if (component) { - return component; - } - parentElement = parentElement.parentElement; - } - return null; +var findParent = (currentComponent) => { + let parentElement = currentComponent.element.parentElement; + while (parentElement) { + const component = componentMapByElement.get(parentElement); + if (component) { + return component; + } + parentElement = parentElement.parentElement; + } + return null; }; +// src/Directive/directives_parser.ts function parseDirectives(content) { - const directives = []; - if (!content) { - return directives; - } - let currentActionName = ''; - let currentArgumentValue = ''; - let currentArguments = []; - let currentModifiers = []; - let state = 'action'; - const getLastActionName = () => { - if (currentActionName) { - return currentActionName; - } - if (directives.length === 0) { - throw new Error('Could not find any directives'); - } - return directives[directives.length - 1].action; - }; - const pushInstruction = () => { - directives.push({ - action: currentActionName, - args: currentArguments, - modifiers: currentModifiers, - getString: () => { - return content; - }, - }); - currentActionName = ''; - currentArgumentValue = ''; - currentArguments = []; - currentModifiers = []; - state = 'action'; - }; - const pushArgument = () => { - currentArguments.push(currentArgumentValue.trim()); - currentArgumentValue = ''; - }; - const pushModifier = () => { - if (currentArguments.length > 1) { - throw new Error(`The modifier "${currentActionName}()" does not support multiple arguments.`); - } - currentModifiers.push({ - name: currentActionName, - value: currentArguments.length > 0 ? currentArguments[0] : null, - }); - currentActionName = ''; - currentArguments = []; - state = 'action'; - }; - for (let i = 0; i < content.length; i++) { - const char = content[i]; - switch (state) { - case 'action': - if (char === '(') { - state = 'arguments'; - break; - } - if (char === ' ') { - if (currentActionName) { - pushInstruction(); - } - break; - } - if (char === '|') { - pushModifier(); - break; - } - currentActionName += char; - break; - case 'arguments': - if (char === ')') { - pushArgument(); - state = 'after_arguments'; - break; - } - if (char === ',') { - pushArgument(); - break; - } - currentArgumentValue += char; - break; - case 'after_arguments': - if (char === '|') { - pushModifier(); - break; - } - if (char !== ' ') { - throw new Error(`Missing space after ${getLastActionName()}()`); - } - pushInstruction(); - break; - } - } - switch (state) { - case 'action': - case 'after_arguments': - if (currentActionName) { - pushInstruction(); - } - break; - default: - throw new Error(`Did you forget to add a closing ")" after "${currentActionName}"?`); - } + const directives = []; + if (!content) { return directives; + } + let currentActionName = ""; + let currentArgumentValue = ""; + let currentArguments = []; + let currentModifiers = []; + let state = "action"; + const getLastActionName = () => { + if (currentActionName) { + return currentActionName; + } + if (directives.length === 0) { + throw new Error("Could not find any directives"); + } + return directives[directives.length - 1].action; + }; + const pushInstruction = () => { + directives.push({ + action: currentActionName, + args: currentArguments, + modifiers: currentModifiers, + getString: () => { + return content; + } + }); + currentActionName = ""; + currentArgumentValue = ""; + currentArguments = []; + currentModifiers = []; + state = "action"; + }; + const pushArgument = () => { + currentArguments.push(currentArgumentValue.trim()); + currentArgumentValue = ""; + }; + const pushModifier = () => { + if (currentArguments.length > 1) { + throw new Error(`The modifier "${currentActionName}()" does not support multiple arguments.`); + } + currentModifiers.push({ + name: currentActionName, + value: currentArguments.length > 0 ? currentArguments[0] : null + }); + currentActionName = ""; + currentArguments = []; + state = "action"; + }; + for (let i = 0; i < content.length; i++) { + const char = content[i]; + switch (state) { + case "action": + if (char === "(") { + state = "arguments"; + break; + } + if (char === " ") { + if (currentActionName) { + pushInstruction(); + } + break; + } + if (char === "|") { + pushModifier(); + break; + } + currentActionName += char; + break; + case "arguments": + if (char === ")") { + pushArgument(); + state = "after_arguments"; + break; + } + if (char === ",") { + pushArgument(); + break; + } + currentArgumentValue += char; + break; + case "after_arguments": + if (char === "|") { + pushModifier(); + break; + } + if (char !== " ") { + throw new Error(`Missing space after ${getLastActionName()}()`); + } + pushInstruction(); + break; + } + } + switch (state) { + case "action": + case "after_arguments": + if (currentActionName) { + pushInstruction(); + } + break; + default: + throw new Error(`Did you forget to add a closing ")" after "${currentActionName}"?`); + } + return directives; } +// src/string_utils.ts function combineSpacedArray(parts) { - const finalParts = []; - parts.forEach((part) => { - finalParts.push(...trimAll(part).split(' ')); - }); - return finalParts; + const finalParts = []; + parts.forEach((part) => { + finalParts.push(...trimAll(part).split(" ")); + }); + return finalParts; } function trimAll(str) { - return str.replace(/[\s]+/g, ' ').trim(); + return str.replace(/[\s]+/g, " ").trim(); } function normalizeModelName(model) { - return (model - .replace(/\[]$/, '') - .split('[') - .map((s) => s.replace(']', '')) - .join('.')); + return model.replace(/\[]$/, "").split("[").map((s) => s.replace("]", "")).join("."); } +// src/dom_utils.ts function getValueFromElement(element, valueStore) { - if (element instanceof HTMLInputElement) { - if (element.type === 'checkbox') { - const modelNameData = getModelDirectiveFromElement(element, false); - if (modelNameData !== null) { - const modelValue = valueStore.get(modelNameData.action); - if (Array.isArray(modelValue)) { - return getMultipleCheckboxValue(element, modelValue); - } - if (Object(modelValue) === modelValue) { - return getMultipleCheckboxValue(element, Object.values(modelValue)); - } - } - if (element.hasAttribute('value')) { - return element.checked ? element.getAttribute('value') : null; - } - return element.checked; - } - return inputValue(element); - } - if (element instanceof HTMLSelectElement) { - if (element.multiple) { - return Array.from(element.selectedOptions).map((el) => el.value); - } - return element.value; - } - if (element.dataset.value) { - return element.dataset.value; - } - if ('value' in element) { - return element.value; - } - if (element.hasAttribute('value')) { - return element.getAttribute('value'); - } - return null; + if (element instanceof HTMLInputElement) { + if (element.type === "checkbox") { + const modelNameData = getModelDirectiveFromElement(element, false); + if (modelNameData !== null) { + const modelValue = valueStore.get(modelNameData.action); + if (Array.isArray(modelValue)) { + return getMultipleCheckboxValue(element, modelValue); + } + if (Object(modelValue) === modelValue) { + return getMultipleCheckboxValue(element, Object.values(modelValue)); + } + } + if (element.hasAttribute("value")) { + return element.checked ? element.getAttribute("value") : null; + } + return element.checked; + } + return inputValue(element); + } + if (element instanceof HTMLSelectElement) { + if (element.multiple) { + return Array.from(element.selectedOptions).map((el) => el.value); + } + return element.value; + } + if (element.dataset.value) { + return element.dataset.value; + } + if ("value" in element) { + return element.value; + } + if (element.hasAttribute("value")) { + return element.getAttribute("value"); + } + return null; } function setValueOnElement(element, value) { - if (element instanceof HTMLInputElement) { - if (element.type === 'file') { - return; - } - if (element.type === 'radio') { - element.checked = element.value == value; - return; - } - if (element.type === 'checkbox') { - if (Array.isArray(value)) { - element.checked = value.some((val) => val == element.value); - } - else if (element.hasAttribute('value')) { - element.checked = element.value == value; - } - else { - element.checked = value; - } - return; - } - } - if (element instanceof HTMLSelectElement) { - const arrayWrappedValue = [].concat(value).map((value) => { - return `${value}`; - }); - Array.from(element.options).forEach((option) => { - option.selected = arrayWrappedValue.includes(option.value); - }); - return; - } - value = value === undefined ? '' : value; - element.value = value; + if (element instanceof HTMLInputElement) { + if (element.type === "file") { + return; + } + if (element.type === "radio") { + element.checked = element.value == value; + return; + } + if (element.type === "checkbox") { + if (Array.isArray(value)) { + element.checked = value.some((val) => val == element.value); + } else if (element.hasAttribute("value")) { + element.checked = element.value == value; + } else { + element.checked = value; + } + return; + } + } + if (element instanceof HTMLSelectElement) { + const arrayWrappedValue = [].concat(value).map((value2) => { + return `${value2}`; + }); + Array.from(element.options).forEach((option) => { + option.selected = arrayWrappedValue.includes(option.value); + }); + return; + } + value = value === void 0 ? "" : value; + element.value = value; } function getAllModelDirectiveFromElements(element) { - if (!element.dataset.model) { - return []; - } - const directives = parseDirectives(element.dataset.model); - directives.forEach((directive) => { - if (directive.args.length > 0) { - throw new Error(`The data-model="${element.dataset.model}" format is invalid: it does not support passing arguments to the model.`); - } - directive.action = normalizeModelName(directive.action); - }); - return directives; + if (!element.dataset.model) { + return []; + } + const directives = parseDirectives(element.dataset.model); + directives.forEach((directive) => { + if (directive.args.length > 0) { + throw new Error( + `The data-model="${element.dataset.model}" format is invalid: it does not support passing arguments to the model.` + ); + } + directive.action = normalizeModelName(directive.action); + }); + return directives; } function getModelDirectiveFromElement(element, throwOnMissing = true) { - const dataModelDirectives = getAllModelDirectiveFromElements(element); - if (dataModelDirectives.length > 0) { - return dataModelDirectives[0]; - } - if (element.getAttribute('name')) { - const formElement = element.closest('form'); - if (formElement && 'model' in formElement.dataset) { - const directives = parseDirectives(formElement.dataset.model || '*'); - const directive = directives[0]; - if (directive.args.length > 0) { - throw new Error(`The data-model="${formElement.dataset.model}" format is invalid: it does not support passing arguments to the model.`); - } - directive.action = normalizeModelName(element.getAttribute('name')); - return directive; - } - } - if (!throwOnMissing) { - return null; - } - throw new Error(`Cannot determine the model name for "${getElementAsTagText(element)}": the element must either have a "data-model" (or "name" attribute living inside a ).`); + const dataModelDirectives = getAllModelDirectiveFromElements(element); + if (dataModelDirectives.length > 0) { + return dataModelDirectives[0]; + } + if (element.getAttribute("name")) { + const formElement = element.closest("form"); + if (formElement && "model" in formElement.dataset) { + const directives = parseDirectives(formElement.dataset.model || "*"); + const directive = directives[0]; + if (directive.args.length > 0) { + throw new Error( + `The data-model="${formElement.dataset.model}" format is invalid: it does not support passing arguments to the model.` + ); + } + directive.action = normalizeModelName(element.getAttribute("name")); + return directive; + } + } + if (!throwOnMissing) { + return null; + } + throw new Error( + `Cannot determine the model name for "${getElementAsTagText( + element + )}": the element must either have a "data-model" (or "name" attribute living inside a ).` + ); } function elementBelongsToThisComponent(element, component) { - if (component.element === element) { - return true; - } - if (!component.element.contains(element)) { - return false; - } - const closestLiveComponent = element.closest('[data-controller~="live"]'); - return closestLiveComponent === component.element; + if (component.element === element) { + return true; + } + if (!component.element.contains(element)) { + return false; + } + const closestLiveComponent = element.closest('[data-controller~="live"]'); + return closestLiveComponent === component.element; } function cloneHTMLElement(element) { - const newElement = element.cloneNode(true); - if (!(newElement instanceof HTMLElement)) { - throw new Error('Could not clone element'); - } - return newElement; + const newElement = element.cloneNode(true); + if (!(newElement instanceof HTMLElement)) { + throw new Error("Could not clone element"); + } + return newElement; } function htmlToElement(html) { - const template = document.createElement('template'); - html = html.trim(); - template.innerHTML = html; - if (template.content.childElementCount > 1) { - throw new Error(`Component HTML contains ${template.content.childElementCount} elements, but only 1 root element is allowed.`); - } - const child = template.content.firstElementChild; - if (!child) { - throw new Error('Child not found'); - } - if (!(child instanceof HTMLElement)) { - throw new Error(`Created element is not an HTMLElement: ${html.trim()}`); - } - return child; + const template = document.createElement("template"); + html = html.trim(); + template.innerHTML = html; + if (template.content.childElementCount > 1) { + throw new Error( + `Component HTML contains ${template.content.childElementCount} elements, but only 1 root element is allowed.` + ); + } + const child = template.content.firstElementChild; + if (!child) { + throw new Error("Child not found"); + } + if (!(child instanceof HTMLElement)) { + throw new Error(`Created element is not an HTMLElement: ${html.trim()}`); + } + return child; } -const getMultipleCheckboxValue = (element, currentValues) => { - const finalValues = [...currentValues]; - const value = inputValue(element); - const index = currentValues.indexOf(value); - if (element.checked) { - if (index === -1) { - finalValues.push(value); - } - return finalValues; - } - if (index > -1) { - finalValues.splice(index, 1); +var getMultipleCheckboxValue = (element, currentValues) => { + const finalValues = [...currentValues]; + const value = inputValue(element); + const index = currentValues.indexOf(value); + if (element.checked) { + if (index === -1) { + finalValues.push(value); } return finalValues; + } + if (index > -1) { + finalValues.splice(index, 1); + } + return finalValues; }; -const inputValue = (element) => element.dataset.value ? element.dataset.value : element.value; +var inputValue = (element) => element.dataset.value ? element.dataset.value : element.value; function isTextualInputElement(el) { - return el instanceof HTMLInputElement && ['text', 'email', 'password', 'search', 'tel', 'url'].includes(el.type); + return el instanceof HTMLInputElement && ["text", "email", "password", "search", "tel", "url"].includes(el.type); } function isTextareaElement(el) { - return el instanceof HTMLTextAreaElement; + return el instanceof HTMLTextAreaElement; } function isNumericalInputElement(element) { - return element instanceof HTMLInputElement && ['number', 'range'].includes(element.type); -} - -class HookManager { - constructor() { - this.hooks = new Map(); - } - register(hookName, callback) { - const hooks = this.hooks.get(hookName) || []; - hooks.push(callback); - this.hooks.set(hookName, hooks); - } - unregister(hookName, callback) { - const hooks = this.hooks.get(hookName) || []; - const index = hooks.indexOf(callback); - if (index === -1) { - return; - } - hooks.splice(index, 1); - this.hooks.set(hookName, hooks); - } - triggerHook(hookName, ...args) { - const hooks = this.hooks.get(hookName) || []; - hooks.forEach((callback) => callback(...args)); - } + return element instanceof HTMLInputElement && ["number", "range"].includes(element.type); } -// base IIFE to define idiomorph -var Idiomorph = (function () { - - //============================================================================= - // AND NOW IT BEGINS... - //============================================================================= - let EMPTY_SET = new Set(); - - // default configuration values, updatable by users now - let defaults = { - morphStyle: "outerHTML", - callbacks : { - beforeNodeAdded: noOp, - afterNodeAdded: noOp, - beforeNodeMorphed: noOp, - afterNodeMorphed: noOp, - beforeNodeRemoved: noOp, - afterNodeRemoved: noOp, - beforeAttributeUpdated: noOp, +// src/HookManager.ts +var HookManager_default = class { + constructor() { + this.hooks = /* @__PURE__ */ new Map(); + } + register(hookName, callback) { + const hooks = this.hooks.get(hookName) || []; + hooks.push(callback); + this.hooks.set(hookName, hooks); + } + unregister(hookName, callback) { + const hooks = this.hooks.get(hookName) || []; + const index = hooks.indexOf(callback); + if (index === -1) { + return; + } + hooks.splice(index, 1); + this.hooks.set(hookName, hooks); + } + triggerHook(hookName, ...args) { + const hooks = this.hooks.get(hookName) || []; + hooks.forEach((callback) => callback(...args)); + } +}; - }, +// ../../../node_modules/.pnpm/idiomorph@0.3.0/node_modules/idiomorph/dist/idiomorph.esm.js +var Idiomorph = function() { + "use strict"; + let EMPTY_SET = /* @__PURE__ */ new Set(); + let defaults = { + morphStyle: "outerHTML", + callbacks: { + beforeNodeAdded: noOp, + afterNodeAdded: noOp, + beforeNodeMorphed: noOp, + afterNodeMorphed: noOp, + beforeNodeRemoved: noOp, + afterNodeRemoved: noOp, + beforeAttributeUpdated: noOp + }, + head: { + style: "merge", + shouldPreserve: function(elt) { + return elt.getAttribute("im-preserve") === "true"; + }, + shouldReAppend: function(elt) { + return elt.getAttribute("im-re-append") === "true"; + }, + shouldRemove: noOp, + afterHeadMorphed: noOp + } + }; + function morph(oldNode, newContent, config = {}) { + if (oldNode instanceof Document) { + oldNode = oldNode.documentElement; + } + if (typeof newContent === "string") { + newContent = parseContent(newContent); + } + let normalizedContent = normalizeContent(newContent); + let ctx = createMorphContext(oldNode, normalizedContent, config); + return morphNormalizedContent(oldNode, normalizedContent, ctx); + } + function morphNormalizedContent(oldNode, normalizedNewContent, ctx) { + if (ctx.head.block) { + let oldHead = oldNode.querySelector("head"); + let newHead = normalizedNewContent.querySelector("head"); + if (oldHead && newHead) { + let promises = handleHeadElement(newHead, oldHead, ctx); + Promise.all(promises).then(function() { + morphNormalizedContent(oldNode, normalizedNewContent, Object.assign(ctx, { head: { - style: 'merge', - shouldPreserve: function (elt) { - return elt.getAttribute("im-preserve") === "true"; - }, - shouldReAppend: function (elt) { - return elt.getAttribute("im-re-append") === "true"; - }, - shouldRemove: noOp, - afterHeadMorphed: noOp, - } - }; - - //============================================================================= - // Core Morphing Algorithm - morph, morphNormalizedContent, morphOldNodeTo, morphChildren - //============================================================================= - function morph(oldNode, newContent, config = {}) { - - if (oldNode instanceof Document) { - oldNode = oldNode.documentElement; - } - - if (typeof newContent === 'string') { - newContent = parseContent(newContent); + block: false, + ignore: true } + })); + }); + return; + } + } + if (ctx.morphStyle === "innerHTML") { + morphChildren(normalizedNewContent, oldNode, ctx); + return oldNode.children; + } else if (ctx.morphStyle === "outerHTML" || ctx.morphStyle == null) { + let bestMatch = findBestNodeMatch(normalizedNewContent, oldNode, ctx); + let previousSibling = bestMatch?.previousSibling; + let nextSibling = bestMatch?.nextSibling; + let morphedNode = morphOldNodeTo(oldNode, bestMatch, ctx); + if (bestMatch) { + return insertSiblings(previousSibling, morphedNode, nextSibling); + } else { + return []; + } + } else { + throw "Do not understand how to morph style " + ctx.morphStyle; + } + } + function ignoreValueOfActiveElement(possibleActiveElement, ctx) { + return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement; + } + function morphOldNodeTo(oldNode, newContent, ctx) { + if (ctx.ignoreActive && oldNode === document.activeElement) { + } else if (newContent == null) { + if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode; + oldNode.remove(); + ctx.callbacks.afterNodeRemoved(oldNode); + return null; + } else if (!isSoftMatch(oldNode, newContent)) { + if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode; + if (ctx.callbacks.beforeNodeAdded(newContent) === false) return oldNode; + oldNode.parentElement.replaceChild(newContent, oldNode); + ctx.callbacks.afterNodeAdded(newContent); + ctx.callbacks.afterNodeRemoved(oldNode); + return newContent; + } else { + if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode; + if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) { + } else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") { + handleHeadElement(newContent, oldNode, ctx); + } else { + syncNodeFrom(newContent, oldNode, ctx); + if (!ignoreValueOfActiveElement(oldNode, ctx)) { + morphChildren(newContent, oldNode, ctx); + } + } + ctx.callbacks.afterNodeMorphed(oldNode, newContent); + return oldNode; + } + } + function morphChildren(newParent, oldParent, ctx) { + let nextNewChild = newParent.firstChild; + let insertionPoint = oldParent.firstChild; + let newChild; + while (nextNewChild) { + newChild = nextNewChild; + nextNewChild = newChild.nextSibling; + if (insertionPoint == null) { + if (ctx.callbacks.beforeNodeAdded(newChild) === false) return; + oldParent.appendChild(newChild); + ctx.callbacks.afterNodeAdded(newChild); + removeIdsFromConsideration(ctx, newChild); + continue; + } + if (isIdSetMatch(newChild, insertionPoint, ctx)) { + morphOldNodeTo(insertionPoint, newChild, ctx); + insertionPoint = insertionPoint.nextSibling; + removeIdsFromConsideration(ctx, newChild); + continue; + } + let idSetMatch = findIdSetMatch(newParent, oldParent, newChild, insertionPoint, ctx); + if (idSetMatch) { + insertionPoint = removeNodesBetween(insertionPoint, idSetMatch, ctx); + morphOldNodeTo(idSetMatch, newChild, ctx); + removeIdsFromConsideration(ctx, newChild); + continue; + } + let softMatch = findSoftMatch(newParent, oldParent, newChild, insertionPoint, ctx); + if (softMatch) { + insertionPoint = removeNodesBetween(insertionPoint, softMatch, ctx); + morphOldNodeTo(softMatch, newChild, ctx); + removeIdsFromConsideration(ctx, newChild); + continue; + } + if (ctx.callbacks.beforeNodeAdded(newChild) === false) return; + oldParent.insertBefore(newChild, insertionPoint); + ctx.callbacks.afterNodeAdded(newChild); + removeIdsFromConsideration(ctx, newChild); + } + while (insertionPoint !== null) { + let tempNode = insertionPoint; + insertionPoint = insertionPoint.nextSibling; + removeNode(tempNode, ctx); + } + } + function ignoreAttribute(attr, to, updateType, ctx) { + if (attr === "value" && ctx.ignoreActiveValue && to === document.activeElement) { + return true; + } + return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false; + } + function syncNodeFrom(from, to, ctx) { + let type = from.nodeType; + if (type === 1) { + const fromAttributes = from.attributes; + const toAttributes = to.attributes; + for (const fromAttribute of fromAttributes) { + if (ignoreAttribute(fromAttribute.name, to, "update", ctx)) { + continue; + } + if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) { + to.setAttribute(fromAttribute.name, fromAttribute.value); + } + } + for (let i = toAttributes.length - 1; 0 <= i; i--) { + const toAttribute = toAttributes[i]; + if (ignoreAttribute(toAttribute.name, to, "remove", ctx)) { + continue; + } + if (!from.hasAttribute(toAttribute.name)) { + to.removeAttribute(toAttribute.name); + } + } + } + if (type === 8 || type === 3) { + if (to.nodeValue !== from.nodeValue) { + to.nodeValue = from.nodeValue; + } + } + if (!ignoreValueOfActiveElement(to, ctx)) { + syncInputValue(from, to, ctx); + } + } + function syncBooleanAttribute(from, to, attributeName, ctx) { + if (from[attributeName] !== to[attributeName]) { + let ignoreUpdate = ignoreAttribute(attributeName, to, "update", ctx); + if (!ignoreUpdate) { + to[attributeName] = from[attributeName]; + } + if (from[attributeName]) { + if (!ignoreUpdate) { + to.setAttribute(attributeName, from[attributeName]); + } + } else { + if (!ignoreAttribute(attributeName, to, "remove", ctx)) { + to.removeAttribute(attributeName); + } + } + } + } + function syncInputValue(from, to, ctx) { + if (from instanceof HTMLInputElement && to instanceof HTMLInputElement && from.type !== "file") { + let fromValue = from.value; + let toValue = to.value; + syncBooleanAttribute(from, to, "checked", ctx); + syncBooleanAttribute(from, to, "disabled", ctx); + if (!from.hasAttribute("value")) { + if (!ignoreAttribute("value", to, "remove", ctx)) { + to.value = ""; + to.removeAttribute("value"); + } + } else if (fromValue !== toValue) { + if (!ignoreAttribute("value", to, "update", ctx)) { + to.setAttribute("value", fromValue); + to.value = fromValue; + } + } + } else if (from instanceof HTMLOptionElement) { + syncBooleanAttribute(from, to, "selected", ctx); + } else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) { + let fromValue = from.value; + let toValue = to.value; + if (ignoreAttribute("value", to, "update", ctx)) { + return; + } + if (fromValue !== toValue) { + to.value = fromValue; + } + if (to.firstChild && to.firstChild.nodeValue !== fromValue) { + to.firstChild.nodeValue = fromValue; + } + } + } + function handleHeadElement(newHeadTag, currentHead, ctx) { + let added = []; + let removed = []; + let preserved = []; + let nodesToAppend = []; + let headMergeStyle = ctx.head.style; + let srcToNewHeadNodes = /* @__PURE__ */ new Map(); + for (const newHeadChild of newHeadTag.children) { + srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild); + } + for (const currentHeadElt of currentHead.children) { + let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML); + let isReAppended = ctx.head.shouldReAppend(currentHeadElt); + let isPreserved = ctx.head.shouldPreserve(currentHeadElt); + if (inNewContent || isPreserved) { + if (isReAppended) { + removed.push(currentHeadElt); + } else { + srcToNewHeadNodes.delete(currentHeadElt.outerHTML); + preserved.push(currentHeadElt); + } + } else { + if (headMergeStyle === "append") { + if (isReAppended) { + removed.push(currentHeadElt); + nodesToAppend.push(currentHeadElt); + } + } else { + if (ctx.head.shouldRemove(currentHeadElt) !== false) { + removed.push(currentHeadElt); + } + } + } + } + nodesToAppend.push(...srcToNewHeadNodes.values()); + log("to append: ", nodesToAppend); + let promises = []; + for (const newNode of nodesToAppend) { + log("adding: ", newNode); + let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild; + log(newElt); + if (ctx.callbacks.beforeNodeAdded(newElt) !== false) { + if (newElt.href || newElt.src) { + let resolve = null; + let promise = new Promise(function(_resolve) { + resolve = _resolve; + }); + newElt.addEventListener("load", function() { + resolve(); + }); + promises.push(promise); + } + currentHead.appendChild(newElt); + ctx.callbacks.afterNodeAdded(newElt); + added.push(newElt); + } + } + for (const removedElement of removed) { + if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) { + currentHead.removeChild(removedElement); + ctx.callbacks.afterNodeRemoved(removedElement); + } + } + ctx.head.afterHeadMorphed(currentHead, { added, kept: preserved, removed }); + return promises; + } + function log() { + } + function noOp() { + } + function mergeDefaults(config) { + let finalConfig = {}; + Object.assign(finalConfig, defaults); + Object.assign(finalConfig, config); + finalConfig.callbacks = {}; + Object.assign(finalConfig.callbacks, defaults.callbacks); + Object.assign(finalConfig.callbacks, config.callbacks); + finalConfig.head = {}; + Object.assign(finalConfig.head, defaults.head); + Object.assign(finalConfig.head, config.head); + return finalConfig; + } + function createMorphContext(oldNode, newContent, config) { + config = mergeDefaults(config); + return { + target: oldNode, + newContent, + config, + morphStyle: config.morphStyle, + ignoreActive: config.ignoreActive, + ignoreActiveValue: config.ignoreActiveValue, + idMap: createIdMap(oldNode, newContent), + deadIds: /* @__PURE__ */ new Set(), + callbacks: config.callbacks, + head: config.head + }; + } + function isIdSetMatch(node1, node2, ctx) { + if (node1 == null || node2 == null) { + return false; + } + if (node1.nodeType === node2.nodeType && node1.tagName === node2.tagName) { + if (node1.id !== "" && node1.id === node2.id) { + return true; + } else { + return getIdIntersectionCount(ctx, node1, node2) > 0; + } + } + return false; + } + function isSoftMatch(node1, node2) { + if (node1 == null || node2 == null) { + return false; + } + return node1.nodeType === node2.nodeType && node1.tagName === node2.tagName; + } + function removeNodesBetween(startInclusive, endExclusive, ctx) { + while (startInclusive !== endExclusive) { + let tempNode = startInclusive; + startInclusive = startInclusive.nextSibling; + removeNode(tempNode, ctx); + } + removeIdsFromConsideration(ctx, endExclusive); + return endExclusive.nextSibling; + } + function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) { + let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent); + let potentialMatch = null; + if (newChildPotentialIdCount > 0) { + let potentialMatch2 = insertionPoint; + let otherMatchCount = 0; + while (potentialMatch2 != null) { + if (isIdSetMatch(newChild, potentialMatch2, ctx)) { + return potentialMatch2; + } + otherMatchCount += getIdIntersectionCount(ctx, potentialMatch2, newContent); + if (otherMatchCount > newChildPotentialIdCount) { + return null; + } + potentialMatch2 = potentialMatch2.nextSibling; + } + } + return potentialMatch; + } + function findSoftMatch(newContent, oldParent, newChild, insertionPoint, ctx) { + let potentialSoftMatch = insertionPoint; + let nextSibling = newChild.nextSibling; + let siblingSoftMatchCount = 0; + while (potentialSoftMatch != null) { + if (getIdIntersectionCount(ctx, potentialSoftMatch, newContent) > 0) { + return null; + } + if (isSoftMatch(newChild, potentialSoftMatch)) { + return potentialSoftMatch; + } + if (isSoftMatch(nextSibling, potentialSoftMatch)) { + siblingSoftMatchCount++; + nextSibling = nextSibling.nextSibling; + if (siblingSoftMatchCount >= 2) { + return null; + } + } + potentialSoftMatch = potentialSoftMatch.nextSibling; + } + return potentialSoftMatch; + } + function parseContent(newContent) { + let parser = new DOMParser(); + let contentWithSvgsRemoved = newContent.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ""); + if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) { + let content = parser.parseFromString(newContent, "text/html"); + if (contentWithSvgsRemoved.match(/<\/html>/)) { + content.generatedByIdiomorph = true; + return content; + } else { + let htmlElement = content.firstChild; + if (htmlElement) { + htmlElement.generatedByIdiomorph = true; + return htmlElement; + } else { + return null; + } + } + } else { + let responseDoc = parser.parseFromString("" + newContent + "", "text/html"); + let content = responseDoc.body.querySelector("template").content; + content.generatedByIdiomorph = true; + return content; + } + } + function normalizeContent(newContent) { + if (newContent == null) { + const dummyParent = document.createElement("div"); + return dummyParent; + } else if (newContent.generatedByIdiomorph) { + return newContent; + } else if (newContent instanceof Node) { + const dummyParent = document.createElement("div"); + dummyParent.append(newContent); + return dummyParent; + } else { + const dummyParent = document.createElement("div"); + for (const elt of [...newContent]) { + dummyParent.append(elt); + } + return dummyParent; + } + } + function insertSiblings(previousSibling, morphedNode, nextSibling) { + let stack = []; + let added = []; + while (previousSibling != null) { + stack.push(previousSibling); + previousSibling = previousSibling.previousSibling; + } + while (stack.length > 0) { + let node = stack.pop(); + added.push(node); + morphedNode.parentElement.insertBefore(node, morphedNode); + } + added.push(morphedNode); + while (nextSibling != null) { + stack.push(nextSibling); + added.push(nextSibling); + nextSibling = nextSibling.nextSibling; + } + while (stack.length > 0) { + morphedNode.parentElement.insertBefore(stack.pop(), morphedNode.nextSibling); + } + return added; + } + function findBestNodeMatch(newContent, oldNode, ctx) { + let currentElement; + currentElement = newContent.firstChild; + let bestElement = currentElement; + let score = 0; + while (currentElement) { + let newScore = scoreElement(currentElement, oldNode, ctx); + if (newScore > score) { + bestElement = currentElement; + score = newScore; + } + currentElement = currentElement.nextSibling; + } + return bestElement; + } + function scoreElement(node1, node2, ctx) { + if (isSoftMatch(node1, node2)) { + return 0.5 + getIdIntersectionCount(ctx, node1, node2); + } + return 0; + } + function removeNode(tempNode, ctx) { + removeIdsFromConsideration(ctx, tempNode); + if (ctx.callbacks.beforeNodeRemoved(tempNode) === false) return; + tempNode.remove(); + ctx.callbacks.afterNodeRemoved(tempNode); + } + function isIdInConsideration(ctx, id) { + return !ctx.deadIds.has(id); + } + function idIsWithinNode(ctx, id, targetNode) { + let idSet = ctx.idMap.get(targetNode) || EMPTY_SET; + return idSet.has(id); + } + function removeIdsFromConsideration(ctx, node) { + let idSet = ctx.idMap.get(node) || EMPTY_SET; + for (const id of idSet) { + ctx.deadIds.add(id); + } + } + function getIdIntersectionCount(ctx, node1, node2) { + let sourceSet = ctx.idMap.get(node1) || EMPTY_SET; + let matchCount = 0; + for (const id of sourceSet) { + if (isIdInConsideration(ctx, id) && idIsWithinNode(ctx, id, node2)) { + ++matchCount; + } + } + return matchCount; + } + function populateIdMapForNode(node, idMap) { + let nodeParent = node.parentElement; + let idElements = node.querySelectorAll("[id]"); + for (const elt of idElements) { + let current = elt; + while (current !== nodeParent && current != null) { + let idSet = idMap.get(current); + if (idSet == null) { + idSet = /* @__PURE__ */ new Set(); + idMap.set(current, idSet); + } + idSet.add(elt.id); + current = current.parentElement; + } + } + } + function createIdMap(oldContent, newContent) { + let idMap = /* @__PURE__ */ new Map(); + populateIdMapForNode(oldContent, idMap); + populateIdMapForNode(newContent, idMap); + return idMap; + } + return { + morph, + defaults + }; +}(); + +// src/normalize_attributes_for_comparison.ts +function normalizeAttributesForComparison(element) { + const isFileInput = element instanceof HTMLInputElement && element.type === "file"; + if (!isFileInput) { + if ("value" in element) { + element.setAttribute("value", element.value); + } else if (element.hasAttribute("value")) { + element.setAttribute("value", ""); + } + } + Array.from(element.children).forEach((child) => { + normalizeAttributesForComparison(child); + }); +} - let normalizedContent = normalizeContent(newContent); +// src/morphdom.ts +var syncAttributes = (fromEl, toEl) => { + for (let i = 0; i < fromEl.attributes.length; i++) { + const attr = fromEl.attributes[i]; + toEl.setAttribute(attr.name, attr.value); + } +}; +function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements, getElementValue, externalMutationTracker) { + const originalElementIdsToSwapAfter = []; + const originalElementsToPreserve = /* @__PURE__ */ new Map(); + const markElementAsNeedingPostMorphSwap = (id, replaceWithClone) => { + const oldElement = originalElementsToPreserve.get(id); + if (!(oldElement instanceof HTMLElement)) { + throw new Error(`Original element with id ${id} not found`); + } + originalElementIdsToSwapAfter.push(id); + if (!replaceWithClone) { + return null; + } + const clonedOldElement = cloneHTMLElement(oldElement); + oldElement.replaceWith(clonedOldElement); + return clonedOldElement; + }; + rootToElement.querySelectorAll("[data-live-preserve]").forEach((newElement) => { + const id = newElement.id; + if (!id) { + throw new Error("The data-live-preserve attribute requires an id attribute to be set on the element"); + } + const oldElement = rootFromElement.querySelector(`#${id}`); + if (!(oldElement instanceof HTMLElement)) { + throw new Error(`The element with id "${id}" was not found in the original HTML`); + } + newElement.removeAttribute("data-live-preserve"); + originalElementsToPreserve.set(id, oldElement); + syncAttributes(newElement, oldElement); + }); + Idiomorph.morph(rootFromElement, rootToElement, { + callbacks: { + beforeNodeMorphed: (fromEl, toEl) => { + if (!(fromEl instanceof Element) || !(toEl instanceof Element)) { + return true; + } + if (fromEl === rootFromElement) { + return true; + } + if (fromEl.id && originalElementsToPreserve.has(fromEl.id)) { + if (fromEl.id === toEl.id) { + return false; + } + const clonedFromEl = markElementAsNeedingPostMorphSwap(fromEl.id, true); + if (!clonedFromEl) { + throw new Error("missing clone"); + } + Idiomorph.morph(clonedFromEl, toEl); + return false; + } + if (fromEl instanceof HTMLElement && toEl instanceof HTMLElement) { + if (typeof fromEl.__x !== "undefined") { + if (!window.Alpine) { + throw new Error( + "Unable to access Alpine.js though the global window.Alpine variable. Please make sure Alpine.js is loaded before Symfony UX LiveComponent." + ); + } + if (typeof window.Alpine.morph !== "function") { + throw new Error( + "Unable to access Alpine.js morph function. Please make sure the Alpine.js Morph plugin is installed and loaded, see https://alpinejs.dev/plugins/morph for more information." + ); + } + window.Alpine.morph(fromEl.__x, toEl); + } + if (externalMutationTracker.wasElementAdded(fromEl)) { + fromEl.insertAdjacentElement("afterend", toEl); + return false; + } + if (modifiedFieldElements.includes(fromEl)) { + setValueOnElement(toEl, getElementValue(fromEl)); + } + if (fromEl === document.activeElement && fromEl !== document.body && null !== getModelDirectiveFromElement(fromEl, false)) { + setValueOnElement(toEl, getElementValue(fromEl)); + } + const elementChanges = externalMutationTracker.getChangedElement(fromEl); + if (elementChanges) { + elementChanges.applyToElement(toEl); + } + if (fromEl.nodeName.toUpperCase() !== "OPTION" && fromEl.isEqualNode(toEl)) { + const normalizedFromEl = cloneHTMLElement(fromEl); + normalizeAttributesForComparison(normalizedFromEl); + const normalizedToEl = cloneHTMLElement(toEl); + normalizeAttributesForComparison(normalizedToEl); + if (normalizedFromEl.isEqualNode(normalizedToEl)) { + return false; + } + } + } + if (fromEl.hasAttribute("data-skip-morph") || fromEl.id && fromEl.id !== toEl.id) { + fromEl.innerHTML = toEl.innerHTML; + return true; + } + if (fromEl.parentElement?.hasAttribute("data-skip-morph")) { + return false; + } + return !fromEl.hasAttribute("data-live-ignore"); + }, + beforeNodeRemoved(node) { + if (!(node instanceof HTMLElement)) { + return true; + } + if (node.id && originalElementsToPreserve.has(node.id)) { + markElementAsNeedingPostMorphSwap(node.id, false); + return true; + } + if (externalMutationTracker.wasElementAdded(node)) { + return false; + } + return !node.hasAttribute("data-live-ignore"); + } + } + }); + originalElementIdsToSwapAfter.forEach((id) => { + const newElement = rootFromElement.querySelector(`#${id}`); + const originalElement = originalElementsToPreserve.get(id); + if (!(newElement instanceof HTMLElement) || !(originalElement instanceof HTMLElement)) { + throw new Error("Missing elements."); + } + newElement.replaceWith(originalElement); + }); +} - let ctx = createMorphContext(oldNode, normalizedContent, config); +// src/Rendering/ChangingItemsTracker.ts +var ChangingItemsTracker_default = class { + constructor() { + // e.g. a Map with key "color" & value { original: 'previousValue', new: 'newValue' }, + this.changedItems = /* @__PURE__ */ new Map(); + this.removedItems = /* @__PURE__ */ new Map(); + } + /** + * A "null" previousValue means the item was NOT previously present. + */ + setItem(itemName, newValue, previousValue) { + if (this.removedItems.has(itemName)) { + const removedRecord = this.removedItems.get(itemName); + this.removedItems.delete(itemName); + if (removedRecord.original === newValue) { + return; + } + } + if (this.changedItems.has(itemName)) { + const originalRecord = this.changedItems.get(itemName); + if (originalRecord.original === newValue) { + this.changedItems.delete(itemName); + return; + } + this.changedItems.set(itemName, { original: originalRecord.original, new: newValue }); + return; + } + this.changedItems.set(itemName, { original: previousValue, new: newValue }); + } + removeItem(itemName, currentValue) { + let trueOriginalValue = currentValue; + if (this.changedItems.has(itemName)) { + const originalRecord = this.changedItems.get(itemName); + trueOriginalValue = originalRecord.original; + this.changedItems.delete(itemName); + if (trueOriginalValue === null) { + return; + } + } + if (!this.removedItems.has(itemName)) { + this.removedItems.set(itemName, { original: trueOriginalValue }); + } + } + getChangedItems() { + return Array.from(this.changedItems, ([name, { new: value }]) => ({ name, value })); + } + getRemovedItems() { + return Array.from(this.removedItems.keys()); + } + isEmpty() { + return this.changedItems.size === 0 && this.removedItems.size === 0; + } +}; - return morphNormalizedContent(oldNode, normalizedContent, ctx); - } +// src/Rendering/ElementChanges.ts +var ElementChanges = class { + constructor() { + this.addedClasses = /* @__PURE__ */ new Set(); + this.removedClasses = /* @__PURE__ */ new Set(); + this.styleChanges = new ChangingItemsTracker_default(); + this.attributeChanges = new ChangingItemsTracker_default(); + } + addClass(className) { + if (!this.removedClasses.delete(className)) { + this.addedClasses.add(className); + } + } + removeClass(className) { + if (!this.addedClasses.delete(className)) { + this.removedClasses.add(className); + } + } + addStyle(styleName, newValue, originalValue) { + this.styleChanges.setItem(styleName, newValue, originalValue); + } + removeStyle(styleName, originalValue) { + this.styleChanges.removeItem(styleName, originalValue); + } + addAttribute(attributeName, newValue, originalValue) { + this.attributeChanges.setItem(attributeName, newValue, originalValue); + } + removeAttribute(attributeName, originalValue) { + this.attributeChanges.removeItem(attributeName, originalValue); + } + getAddedClasses() { + return [...this.addedClasses]; + } + getRemovedClasses() { + return [...this.removedClasses]; + } + getChangedStyles() { + return this.styleChanges.getChangedItems(); + } + getRemovedStyles() { + return this.styleChanges.getRemovedItems(); + } + getChangedAttributes() { + return this.attributeChanges.getChangedItems(); + } + getRemovedAttributes() { + return this.attributeChanges.getRemovedItems(); + } + applyToElement(element) { + element.classList.add(...this.addedClasses); + element.classList.remove(...this.removedClasses); + this.styleChanges.getChangedItems().forEach((change) => { + element.style.setProperty(change.name, change.value); + return; + }); + this.styleChanges.getRemovedItems().forEach((styleName) => { + element.style.removeProperty(styleName); + }); + this.attributeChanges.getChangedItems().forEach((change) => { + element.setAttribute(change.name, change.value); + }); + this.attributeChanges.getRemovedItems().forEach((attributeName) => { + element.removeAttribute(attributeName); + }); + } + isEmpty() { + return this.addedClasses.size === 0 && this.removedClasses.size === 0 && this.styleChanges.isEmpty() && this.attributeChanges.isEmpty(); + } +}; - function morphNormalizedContent(oldNode, normalizedNewContent, ctx) { - if (ctx.head.block) { - let oldHead = oldNode.querySelector('head'); - let newHead = normalizedNewContent.querySelector('head'); - if (oldHead && newHead) { - let promises = handleHeadElement(newHead, oldHead, ctx); - // when head promises resolve, call morph again, ignoring the head tag - Promise.all(promises).then(function () { - morphNormalizedContent(oldNode, normalizedNewContent, Object.assign(ctx, { - head: { - block: false, - ignore: true - } - })); - }); - return; - } - } +// src/Rendering/ExternalMutationTracker.ts +var ExternalMutationTracker_default = class { + constructor(element, shouldTrackChangeCallback) { + this.changedElements = /* @__PURE__ */ new WeakMap(); + /** For testing */ + this.changedElementsCount = 0; + this.addedElements = []; + this.removedElements = []; + this.isStarted = false; + this.element = element; + this.shouldTrackChangeCallback = shouldTrackChangeCallback; + this.mutationObserver = new MutationObserver(this.onMutations.bind(this)); + } + start() { + if (this.isStarted) { + return; + } + this.mutationObserver.observe(this.element, { + childList: true, + subtree: true, + attributes: true, + attributeOldValue: true + }); + this.isStarted = true; + } + stop() { + if (this.isStarted) { + this.mutationObserver.disconnect(); + this.isStarted = false; + } + } + getChangedElement(element) { + return this.changedElements.has(element) ? this.changedElements.get(element) : null; + } + getAddedElements() { + return this.addedElements; + } + wasElementAdded(element) { + return this.addedElements.includes(element); + } + /** + * Forces any pending mutations to be handled immediately, then clears the queue. + */ + handlePendingChanges() { + this.onMutations(this.mutationObserver.takeRecords()); + } + onMutations(mutations) { + const handledAttributeMutations = /* @__PURE__ */ new WeakMap(); + for (const mutation of mutations) { + const element = mutation.target; + if (!this.shouldTrackChangeCallback(element)) { + continue; + } + if (this.isElementAddedByTranslation(element)) { + continue; + } + let isChangeInAddedElement = false; + for (const addedElement of this.addedElements) { + if (addedElement.contains(element)) { + isChangeInAddedElement = true; + break; + } + } + if (isChangeInAddedElement) { + continue; + } + switch (mutation.type) { + case "childList": + this.handleChildListMutation(mutation); + break; + case "attributes": + if (!handledAttributeMutations.has(element)) { + handledAttributeMutations.set(element, []); + } + if (!handledAttributeMutations.get(element).includes(mutation.attributeName)) { + this.handleAttributeMutation(mutation); + handledAttributeMutations.set(element, [ + ...handledAttributeMutations.get(element), + mutation.attributeName + ]); + } + break; + } + } + } + handleChildListMutation(mutation) { + mutation.addedNodes.forEach((node) => { + if (!(node instanceof Element)) { + return; + } + if (this.removedElements.includes(node)) { + this.removedElements.splice(this.removedElements.indexOf(node), 1); + return; + } + if (this.isElementAddedByTranslation(node)) { + return; + } + this.addedElements.push(node); + }); + mutation.removedNodes.forEach((node) => { + if (!(node instanceof Element)) { + return; + } + if (this.addedElements.includes(node)) { + this.addedElements.splice(this.addedElements.indexOf(node), 1); + return; + } + this.removedElements.push(node); + }); + } + handleAttributeMutation(mutation) { + const element = mutation.target; + if (!this.changedElements.has(element)) { + this.changedElements.set(element, new ElementChanges()); + this.changedElementsCount++; + } + const changedElement = this.changedElements.get(element); + switch (mutation.attributeName) { + case "class": + this.handleClassAttributeMutation(mutation, changedElement); + break; + case "style": + this.handleStyleAttributeMutation(mutation, changedElement); + break; + default: + this.handleGenericAttributeMutation(mutation, changedElement); + } + if (changedElement.isEmpty()) { + this.changedElements.delete(element); + this.changedElementsCount--; + } + } + handleClassAttributeMutation(mutation, elementChanges) { + const element = mutation.target; + const previousValue = mutation.oldValue || ""; + const previousValues = previousValue.match(/(\S+)/gu) || []; + const newValues = [].slice.call(element.classList); + const addedValues = newValues.filter((value) => !previousValues.includes(value)); + const removedValues = previousValues.filter((value) => !newValues.includes(value)); + addedValues.forEach((value) => { + elementChanges.addClass(value); + }); + removedValues.forEach((value) => { + elementChanges.removeClass(value); + }); + } + handleStyleAttributeMutation(mutation, elementChanges) { + const element = mutation.target; + const previousValue = mutation.oldValue || ""; + const previousStyles = this.extractStyles(previousValue); + const newValue = element.getAttribute("style") || ""; + const newStyles = this.extractStyles(newValue); + const addedOrChangedStyles = Object.keys(newStyles).filter( + (key) => previousStyles[key] === void 0 || previousStyles[key] !== newStyles[key] + ); + const removedStyles = Object.keys(previousStyles).filter((key) => !newStyles[key]); + addedOrChangedStyles.forEach((style) => { + elementChanges.addStyle( + style, + newStyles[style], + previousStyles[style] === void 0 ? null : previousStyles[style] + ); + }); + removedStyles.forEach((style) => { + elementChanges.removeStyle(style, previousStyles[style]); + }); + } + handleGenericAttributeMutation(mutation, elementChanges) { + const attributeName = mutation.attributeName; + const element = mutation.target; + let oldValue = mutation.oldValue; + let newValue = element.getAttribute(attributeName); + if (oldValue === attributeName) { + oldValue = ""; + } + if (newValue === attributeName) { + newValue = ""; + } + if (!element.hasAttribute(attributeName)) { + if (oldValue === null) { + return; + } + elementChanges.removeAttribute(attributeName, mutation.oldValue); + return; + } + if (newValue === oldValue) { + return; + } + elementChanges.addAttribute(attributeName, element.getAttribute(attributeName), mutation.oldValue); + } + extractStyles(styles) { + const styleObject = {}; + styles.split(";").forEach((style) => { + const parts = style.split(":"); + if (parts.length === 1) { + return; + } + const property = parts[0].trim(); + styleObject[property] = parts.slice(1).join(":").trim(); + }); + return styleObject; + } + /** + * Helps avoid tracking changes by Chrome's translation feature. + * + * When Chrome translates, it mutates the dom in a way that triggers MutationObserver. + * This includes adding new elements wrapped in a tag. This causes live + * components to incorrectly think that these new elements should persist through + * re-renders, causing duplicate text. + */ + isElementAddedByTranslation(element) { + return element.tagName === "FONT" && element.getAttribute("style") === "vertical-align: inherit;"; + } +}; - if (ctx.morphStyle === "innerHTML") { +// src/Component/UnsyncedInputsTracker.ts +var UnsyncedInputsTracker_default = class { + constructor(component, modelElementResolver) { + this.elementEventListeners = [ + { event: "input", callback: (event) => this.handleInputEvent(event) } + ]; + this.component = component; + this.modelElementResolver = modelElementResolver; + this.unsyncedInputs = new UnsyncedInputContainer(); + } + activate() { + this.elementEventListeners.forEach(({ event, callback }) => { + this.component.element.addEventListener(event, callback); + }); + } + deactivate() { + this.elementEventListeners.forEach(({ event, callback }) => { + this.component.element.removeEventListener(event, callback); + }); + } + markModelAsSynced(modelName) { + this.unsyncedInputs.markModelAsSynced(modelName); + } + handleInputEvent(event) { + const target = event.target; + if (!target) { + return; + } + this.updateModelFromElement(target); + } + updateModelFromElement(element) { + if (!elementBelongsToThisComponent(element, this.component)) { + return; + } + if (!(element instanceof HTMLElement)) { + throw new Error("Could not update model for non HTMLElement"); + } + const modelName = this.modelElementResolver.getModelName(element); + this.unsyncedInputs.add(element, modelName); + } + getUnsyncedInputs() { + return this.unsyncedInputs.allUnsyncedInputs(); + } + getUnsyncedModels() { + return Array.from(this.unsyncedInputs.getUnsyncedModelNames()); + } + resetUnsyncedFields() { + this.unsyncedInputs.resetUnsyncedFields(); + } +}; +var UnsyncedInputContainer = class { + constructor() { + this.unsyncedNonModelFields = []; + this.unsyncedModelNames = []; + this.unsyncedModelFields = /* @__PURE__ */ new Map(); + } + add(element, modelName = null) { + if (modelName) { + this.unsyncedModelFields.set(modelName, element); + if (!this.unsyncedModelNames.includes(modelName)) { + this.unsyncedModelNames.push(modelName); + } + return; + } + this.unsyncedNonModelFields.push(element); + } + /** + * Mark all fields as synced, except for those not bound to a model or whose + * values are still dirty. + */ + resetUnsyncedFields() { + this.unsyncedModelFields.forEach((value, key) => { + if (!this.unsyncedModelNames.includes(key)) { + this.unsyncedModelFields.delete(key); + } + }); + } + allUnsyncedInputs() { + return [...this.unsyncedNonModelFields, ...this.unsyncedModelFields.values()]; + } + markModelAsSynced(modelName) { + const index = this.unsyncedModelNames.indexOf(modelName); + if (index !== -1) { + this.unsyncedModelNames.splice(index, 1); + } + } + /** + * Returns a list of models whose fields have been modified, but whose values + * have not yet been set onto the data store. + */ + getUnsyncedModelNames() { + return this.unsyncedModelNames; + } +}; - // innerHTML, so we are only updating the children - morphChildren(normalizedNewContent, oldNode, ctx); - return oldNode.children; +// src/data_manipulation_utils.ts +function getDeepData(data, propertyPath) { + const { currentLevelData, finalKey } = parseDeepData(data, propertyPath); + if (currentLevelData === void 0) { + return void 0; + } + return currentLevelData[finalKey]; +} +var parseDeepData = (data, propertyPath) => { + const finalData = JSON.parse(JSON.stringify(data)); + let currentLevelData = finalData; + const parts = propertyPath.split("."); + for (let i = 0; i < parts.length - 1; i++) { + currentLevelData = currentLevelData[parts[i]]; + } + const finalKey = parts[parts.length - 1]; + return { + currentLevelData, + finalData, + finalKey, + parts + }; +}; - } else if (ctx.morphStyle === "outerHTML" || ctx.morphStyle == null) { - // otherwise find the best element match in the new content, morph that, and merge its siblings - // into either side of the best match - let bestMatch = findBestNodeMatch(normalizedNewContent, oldNode, ctx); +// src/Component/ValueStore.ts +var ValueStore_default = class { + constructor(props) { + /** + * Original, read-only props that represent the original component state. + * + * @private + */ + this.props = {}; + /** + * A list of props that have been "dirty" (changed) since the last request to the server. + */ + this.dirtyProps = {}; + /** + * A list of dirty props that were sent to the server, but the response has + * not yet been received. + */ + this.pendingProps = {}; + /** + * A list of props that the parent wants us to update. + * + * These will be sent on the next request to the server. + */ + this.updatedPropsFromParent = {}; + this.props = props; + } + /** + * Returns the props with the given name. + * + * This allows for non-normalized model names - e.g. + * user[firstName] -> user.firstName and also will fetch + * deeply (fetching the "firstName" sub-key from the "user" key). + */ + get(name) { + const normalizedName = normalizeModelName(name); + if (this.dirtyProps[normalizedName] !== void 0) { + return this.dirtyProps[normalizedName]; + } + if (this.pendingProps[normalizedName] !== void 0) { + return this.pendingProps[normalizedName]; + } + if (this.props[normalizedName] !== void 0) { + return this.props[normalizedName]; + } + return getDeepData(this.props, normalizedName); + } + has(name) { + return this.get(name) !== void 0; + } + /** + * Sets data back onto the value store. + * + * The name can be in the non-normalized format. + * + * Returns true if the new value is different from the existing value. + */ + set(name, value) { + const normalizedName = normalizeModelName(name); + if (this.get(normalizedName) === value) { + return false; + } + this.dirtyProps[normalizedName] = value; + return true; + } + getOriginalProps() { + return { ...this.props }; + } + getDirtyProps() { + return { ...this.dirtyProps }; + } + getUpdatedPropsFromParent() { + return { ...this.updatedPropsFromParent }; + } + /** + * Called when an update request begins. + */ + flushDirtyPropsToPending() { + this.pendingProps = { ...this.dirtyProps }; + this.dirtyProps = {}; + } + /** + * Called when an update request finishes successfully. + */ + reinitializeAllProps(props) { + this.props = props; + this.updatedPropsFromParent = {}; + this.pendingProps = {}; + } + /** + * Called after an update request failed. + */ + pushPendingPropsBackToDirty() { + this.dirtyProps = { ...this.pendingProps, ...this.dirtyProps }; + this.pendingProps = {}; + } + /** + * This is used when a parent component is rendering, and it includes + * a fresh set of props that should be updated on the child component. + * + * The server manages returning only the props that should be updated onto + * the child, so we don't need to worry about that. + * + * The props are stored in a different place, because the existing props + * have their own checksum and these new props have *their* own checksum. + * So, on the next render, both need to be sent independently. + * + * Returns true if any of the props are different. + */ + storeNewPropsFromParent(props) { + let changed = false; + for (const [key, value] of Object.entries(props)) { + const currentValue = this.get(key); + if (currentValue !== value) { + changed = true; + } + } + if (changed) { + this.updatedPropsFromParent = props; + } + return changed; + } +}; - // stash the siblings that will need to be inserted on either side of the best match - let previousSibling = bestMatch?.previousSibling; - let nextSibling = bestMatch?.nextSibling; +// src/Component/index.ts +var Component = class { + /** + * @param element The root element + * @param name The name of the component + * @param props Readonly component props + * @param listeners Array of event -> action listeners + * @param id Some unique id to identify this component. Needed to be a child component + * @param backend Backend instance for updating + * @param elementDriver Class to get "model" name from any element. + */ + constructor(element, name, props, listeners, id, backend, elementDriver) { + /** + * A fingerprint that identifies the props/input that was used on + * the server to create this component, especially if it was a + * child component. This is sent back to the server and can be used + * to determine if any "input" to the child component changed and thus, + * if the child component needs to be re-rendered. + */ + this.fingerprint = ""; + this.defaultDebounce = 150; + this.backendRequest = null; + /** Actions that are waiting to be executed */ + this.pendingActions = []; + /** Files that are waiting to be sent */ + this.pendingFiles = {}; + /** Is a request waiting to be made? */ + this.isRequestPending = false; + /** Current "timeout" before the pending request should be sent. */ + this.requestDebounceTimeout = null; + this.element = element; + this.name = name; + this.backend = backend; + this.elementDriver = elementDriver; + this.id = id; + this.listeners = /* @__PURE__ */ new Map(); + listeners.forEach((listener) => { + if (!this.listeners.has(listener.event)) { + this.listeners.set(listener.event, []); + } + this.listeners.get(listener.event)?.push(listener.action); + }); + this.valueStore = new ValueStore_default(props); + this.unsyncedInputsTracker = new UnsyncedInputsTracker_default(this, elementDriver); + this.hooks = new HookManager_default(); + this.resetPromise(); + this.externalMutationTracker = new ExternalMutationTracker_default( + this.element, + (element2) => elementBelongsToThisComponent(element2, this) + ); + this.externalMutationTracker.start(); + } + addPlugin(plugin) { + plugin.attachToComponent(this); + } + connect() { + registerComponent(this); + this.hooks.triggerHook("connect", this); + this.unsyncedInputsTracker.activate(); + this.externalMutationTracker.start(); + } + disconnect() { + unregisterComponent(this); + this.hooks.triggerHook("disconnect", this); + this.clearRequestDebounceTimeout(); + this.unsyncedInputsTracker.deactivate(); + this.externalMutationTracker.stop(); + } + on(hookName, callback) { + this.hooks.register(hookName, callback); + } + off(hookName, callback) { + this.hooks.unregister(hookName, callback); + } + set(model, value, reRender = false, debounce = false) { + const promise = this.nextRequestPromise; + const modelName = normalizeModelName(model); + if (!this.valueStore.has(modelName)) { + throw new Error(`Invalid model name "${model}".`); + } + const isChanged = this.valueStore.set(modelName, value); + this.hooks.triggerHook("model:set", model, value, this); + this.unsyncedInputsTracker.markModelAsSynced(modelName); + if (reRender && isChanged) { + this.debouncedStartRequest(debounce); + } + return promise; + } + getData(model) { + const modelName = normalizeModelName(model); + if (!this.valueStore.has(modelName)) { + throw new Error(`Invalid model "${model}".`); + } + return this.valueStore.get(modelName); + } + action(name, args = {}, debounce = false) { + const promise = this.nextRequestPromise; + this.pendingActions.push({ + name, + args + }); + this.debouncedStartRequest(debounce); + return promise; + } + files(key, input) { + this.pendingFiles[key] = input; + } + render() { + const promise = this.nextRequestPromise; + this.tryStartingRequest(); + return promise; + } + /** + * Returns an array of models the user has modified, but whose model has not + * yet been updated. + */ + getUnsyncedModels() { + return this.unsyncedInputsTracker.getUnsyncedModels(); + } + emit(name, data, onlyMatchingComponentsNamed = null) { + this.performEmit(name, data, false, onlyMatchingComponentsNamed); + } + emitUp(name, data, onlyMatchingComponentsNamed = null) { + this.performEmit(name, data, true, onlyMatchingComponentsNamed); + } + emitSelf(name, data) { + this.doEmit(name, data); + } + performEmit(name, data, emitUp, matchingName) { + const components = findComponents(this, emitUp, matchingName); + components.forEach((component) => { + component.doEmit(name, data); + }); + } + doEmit(name, data) { + if (!this.listeners.has(name)) { + return; + } + const actions = this.listeners.get(name) || []; + actions.forEach((action) => { + this.action(action, data, 1); + }); + } + isTurboEnabled() { + return typeof Turbo !== "undefined" && !this.element.closest('[data-turbo="false"]'); + } + tryStartingRequest() { + if (!this.backendRequest) { + this.performRequest(); + return; + } + this.isRequestPending = true; + } + performRequest() { + const thisPromiseResolve = this.nextRequestPromiseResolve; + this.resetPromise(); + this.unsyncedInputsTracker.resetUnsyncedFields(); + const filesToSend = {}; + for (const [key, value] of Object.entries(this.pendingFiles)) { + if (value.files) { + filesToSend[key] = value.files; + } + } + const requestConfig = { + props: this.valueStore.getOriginalProps(), + actions: this.pendingActions, + updated: this.valueStore.getDirtyProps(), + children: {}, + updatedPropsFromParent: this.valueStore.getUpdatedPropsFromParent(), + files: filesToSend + }; + this.hooks.triggerHook("request:started", requestConfig); + this.backendRequest = this.backend.makeRequest( + requestConfig.props, + requestConfig.actions, + requestConfig.updated, + requestConfig.children, + requestConfig.updatedPropsFromParent, + requestConfig.files + ); + this.hooks.triggerHook("loading.state:started", this.element, this.backendRequest); + this.pendingActions = []; + this.valueStore.flushDirtyPropsToPending(); + this.isRequestPending = false; + this.backendRequest.promise.then(async (response) => { + const backendResponse = new BackendResponse_default(response); + const html = await backendResponse.getBody(); + for (const input of Object.values(this.pendingFiles)) { + input.value = ""; + } + const headers = backendResponse.response.headers; + if (!headers.get("Content-Type")?.includes("application/vnd.live-component+html") && !headers.get("X-Live-Redirect")) { + const controls = { displayError: true }; + this.valueStore.pushPendingPropsBackToDirty(); + this.hooks.triggerHook("response:error", backendResponse, controls); + if (controls.displayError) { + this.renderError(html); + } + this.backendRequest = null; + thisPromiseResolve(backendResponse); + return response; + } + this.processRerender(html, backendResponse); + const liveUrl = backendResponse.getLiveUrl(); + if (liveUrl) { + history.replaceState( + history.state, + "", + new URL(liveUrl + window.location.hash, window.location.origin) + ); + } + this.backendRequest = null; + thisPromiseResolve(backendResponse); + if (this.isRequestPending) { + this.isRequestPending = false; + this.performRequest(); + } + return response; + }); + } + processRerender(html, backendResponse) { + const controls = { shouldRender: true }; + this.hooks.triggerHook("render:started", html, backendResponse, controls); + if (!controls.shouldRender) { + return; + } + if (backendResponse.response.headers.get("Location")) { + if (this.isTurboEnabled()) { + Turbo.visit(backendResponse.response.headers.get("Location")); + } else { + window.location.href = backendResponse.response.headers.get("Location") || ""; + } + return; + } + this.hooks.triggerHook("loading.state:finished", this.element); + const modifiedModelValues = {}; + Object.keys(this.valueStore.getDirtyProps()).forEach((modelName) => { + modifiedModelValues[modelName] = this.valueStore.get(modelName); + }); + let newElement; + try { + newElement = htmlToElement(html); + if (!newElement.matches("[data-controller~=live]")) { + throw new Error("A live component template must contain a single root controller element."); + } + } catch (error) { + console.error(`There was a problem with the '${this.name}' component HTML returned:`, { + id: this.id + }); + throw error; + } + this.externalMutationTracker.handlePendingChanges(); + this.externalMutationTracker.stop(); + executeMorphdom( + this.element, + newElement, + this.unsyncedInputsTracker.getUnsyncedInputs(), + (element) => getValueFromElement(element, this.valueStore), + this.externalMutationTracker + ); + this.externalMutationTracker.start(); + const newProps = this.elementDriver.getComponentProps(); + this.valueStore.reinitializeAllProps(newProps); + const eventsToEmit = this.elementDriver.getEventsToEmit(); + const browserEventsToDispatch = this.elementDriver.getBrowserEventsToDispatch(); + Object.keys(modifiedModelValues).forEach((modelName) => { + this.valueStore.set(modelName, modifiedModelValues[modelName]); + }); + eventsToEmit.forEach(({ event, data, target, componentName }) => { + if (target === "up") { + this.emitUp(event, data, componentName); + return; + } + if (target === "self") { + this.emitSelf(event, data); + return; + } + this.emit(event, data, componentName); + }); + browserEventsToDispatch.forEach(({ event, payload }) => { + this.element.dispatchEvent( + new CustomEvent(event, { + detail: payload, + bubbles: true + }) + ); + }); + this.hooks.triggerHook("render:finished", this); + } + calculateDebounce(debounce) { + if (debounce === true) { + return this.defaultDebounce; + } + if (debounce === false) { + return 0; + } + return debounce; + } + clearRequestDebounceTimeout() { + if (this.requestDebounceTimeout) { + clearTimeout(this.requestDebounceTimeout); + this.requestDebounceTimeout = null; + } + } + debouncedStartRequest(debounce) { + this.clearRequestDebounceTimeout(); + this.requestDebounceTimeout = window.setTimeout(() => { + this.render(); + }, this.calculateDebounce(debounce)); + } + // inspired by Livewire! + renderError(html) { + let modal = document.getElementById("live-component-error"); + if (modal) { + modal.innerHTML = ""; + } else { + modal = document.createElement("div"); + modal.id = "live-component-error"; + modal.style.padding = "50px"; + modal.style.backgroundColor = "rgba(0, 0, 0, .5)"; + modal.style.zIndex = "100000"; + modal.style.position = "fixed"; + modal.style.top = "0px"; + modal.style.bottom = "0px"; + modal.style.left = "0px"; + modal.style.right = "0px"; + modal.style.display = "flex"; + modal.style.flexDirection = "column"; + } + const iframe = document.createElement("iframe"); + iframe.style.borderRadius = "5px"; + iframe.style.flexGrow = "1"; + modal.appendChild(iframe); + document.body.prepend(modal); + document.body.style.overflow = "hidden"; + if (iframe.contentWindow) { + iframe.contentWindow.document.open(); + iframe.contentWindow.document.write(html); + iframe.contentWindow.document.close(); + } + const closeModal = (modal2) => { + if (modal2) { + modal2.outerHTML = ""; + } + document.body.style.overflow = "visible"; + }; + modal.addEventListener("click", () => closeModal(modal)); + modal.setAttribute("tabindex", "0"); + modal.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + closeModal(modal); + } + }); + modal.focus(); + } + resetPromise() { + this.nextRequestPromise = new Promise((resolve) => { + this.nextRequestPromiseResolve = resolve; + }); + } + /** + * Called on a child component after the parent component render has requested + * that the child component update its props & re-render if necessary. + */ + _updateFromParentProps(props) { + const isChanged = this.valueStore.storeNewPropsFromParent(props); + if (isChanged) { + this.render(); + } + } +}; +function proxifyComponent(component) { + return new Proxy(component, { + get(component2, prop) { + if (prop in component2 || typeof prop !== "string") { + if (typeof component2[prop] === "function") { + const callable = component2[prop]; + return (...args) => { + return callable.apply(component2, args); + }; + } + return Reflect.get(component2, prop); + } + if (component2.valueStore.has(prop)) { + return component2.getData(prop); + } + return (args) => { + return component2.action.apply(component2, [prop, args]); + }; + }, + set(target, property, value) { + if (property in target) { + target[property] = value; + return true; + } + target.set(property, value); + return true; + } + }); +} - // morph it - let morphedNode = morphOldNodeTo(oldNode, bestMatch, ctx); +// src/Component/ElementDriver.ts +var StimulusElementDriver = class { + constructor(controller) { + this.controller = controller; + } + getModelName(element) { + const modelDirective = getModelDirectiveFromElement(element, false); + if (!modelDirective) { + return null; + } + return modelDirective.action; + } + getComponentProps() { + return this.controller.propsValue; + } + getEventsToEmit() { + return this.controller.eventsToEmitValue; + } + getBrowserEventsToDispatch() { + return this.controller.eventsToDispatchValue; + } +}; - if (bestMatch) { - // if there was a best match, merge the siblings in too and return the - // whole bunch - return insertSiblings(previousSibling, morphedNode, nextSibling); - } else { - // otherwise nothing was added to the DOM - return [] - } - } else { - throw "Do not understand how to morph style " + ctx.morphStyle; - } - } +// src/Directive/get_model_binding.ts +function get_model_binding_default(modelDirective) { + let shouldRender = true; + let targetEventName = null; + let debounce = false; + let minLength = null; + let maxLength = null; + let minValue = null; + let maxValue = null; + modelDirective.modifiers.forEach((modifier) => { + switch (modifier.name) { + case "on": + if (!modifier.value) { + throw new Error( + `The "on" modifier in ${modelDirective.getString()} requires a value - e.g. on(change).` + ); + } + if (!["input", "change"].includes(modifier.value)) { + throw new Error( + `The "on" modifier in ${modelDirective.getString()} only accepts the arguments "input" or "change".` + ); + } + targetEventName = modifier.value; + break; + case "norender": + shouldRender = false; + break; + case "debounce": + debounce = modifier.value ? Number.parseInt(modifier.value) : true; + break; + case "min_length": + minLength = modifier.value ? Number.parseInt(modifier.value) : null; + break; + case "max_length": + maxLength = modifier.value ? Number.parseInt(modifier.value) : null; + break; + case "min_value": + minValue = modifier.value ? Number.parseFloat(modifier.value) : null; + break; + case "max_value": + maxValue = modifier.value ? Number.parseFloat(modifier.value) : null; + break; + default: + throw new Error(`Unknown modifier "${modifier.name}" in data-model="${modelDirective.getString()}".`); + } + }); + const [modelName, innerModelName] = modelDirective.action.split(":"); + return { + modelName, + innerModelName: innerModelName || null, + shouldRender, + debounce, + targetEventName, + minLength, + maxLength, + minValue, + maxValue + }; +} +// src/Component/plugins/ChildComponentPlugin.ts +var ChildComponentPlugin_default = class { + constructor(component) { + this.parentModelBindings = []; + this.component = component; + const modelDirectives = getAllModelDirectiveFromElements(this.component.element); + this.parentModelBindings = modelDirectives.map(get_model_binding_default); + } + attachToComponent(component) { + component.on("request:started", (requestData) => { + requestData.children = this.getChildrenFingerprints(); + }); + component.on("model:set", (model, value) => { + this.notifyParentModelChange(model, value); + }); + } + getChildrenFingerprints() { + const fingerprints = {}; + this.getChildren().forEach((child) => { + if (!child.id) { + throw new Error("missing id"); + } + fingerprints[child.id] = { + fingerprint: child.fingerprint, + tag: child.element.tagName.toLowerCase() + }; + }); + return fingerprints; + } + /** + * Notifies parent of a model change if desired. + * + * This makes the child "behave" like it's a normal `` element, + * where, when its value changes, the parent is notified. + */ + notifyParentModelChange(modelName, value) { + const parentComponent = findParent(this.component); + if (!parentComponent) { + return; + } + this.parentModelBindings.forEach((modelBinding) => { + const childModelName = modelBinding.innerModelName || "value"; + if (childModelName !== modelName) { + return; + } + parentComponent.set(modelBinding.modelName, value, modelBinding.shouldRender, modelBinding.debounce); + }); + } + getChildren() { + return findChildren(this.component); + } +}; - /** - * @param possibleActiveElement - * @param ctx - * @returns {boolean} - */ - function ignoreValueOfActiveElement(possibleActiveElement, ctx) { - return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement; - } +// src/Component/plugins/LazyPlugin.ts +var LazyPlugin_default = class { + constructor() { + this.intersectionObserver = null; + } + attachToComponent(component) { + if ("lazy" !== component.element.attributes.getNamedItem("loading")?.value) { + return; + } + component.on("connect", () => { + this.getObserver().observe(component.element); + }); + component.on("disconnect", () => { + this.intersectionObserver?.unobserve(component.element); + }); + } + getObserver() { + if (!this.intersectionObserver) { + this.intersectionObserver = new IntersectionObserver((entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + entry.target.dispatchEvent(new CustomEvent("live:appear")); + observer.unobserve(entry.target); + } + }); + }); + } + return this.intersectionObserver; + } +}; - /** - * @param oldNode root node to merge content into - * @param newContent new content to merge - * @param ctx the merge context - * @returns {Element} the element that ended up in the DOM - */ - function morphOldNodeTo(oldNode, newContent, ctx) { - if (ctx.ignoreActive && oldNode === document.activeElement) ; else if (newContent == null) { - if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode; +// src/Component/plugins/LoadingPlugin.ts +var LoadingPlugin_default = class { + attachToComponent(component) { + component.on("loading.state:started", (element, request) => { + this.startLoading(component, element, request); + }); + component.on("loading.state:finished", (element) => { + this.finishLoading(component, element); + }); + this.finishLoading(component, component.element); + } + startLoading(component, targetElement, backendRequest) { + this.handleLoadingToggle(component, true, targetElement, backendRequest); + } + finishLoading(component, targetElement) { + this.handleLoadingToggle(component, false, targetElement, null); + } + handleLoadingToggle(component, isLoading, targetElement, backendRequest) { + if (isLoading) { + this.addAttributes(targetElement, ["busy"]); + } else { + this.removeAttributes(targetElement, ["busy"]); + } + this.getLoadingDirectives(component, targetElement).forEach(({ element, directives }) => { + if (isLoading) { + this.addAttributes(element, ["data-live-is-loading"]); + } else { + this.removeAttributes(element, ["data-live-is-loading"]); + } + directives.forEach((directive) => { + this.handleLoadingDirective(element, isLoading, directive, backendRequest); + }); + }); + } + handleLoadingDirective(element, isLoading, directive, backendRequest) { + const finalAction = parseLoadingAction(directive.action, isLoading); + const targetedActions = []; + const targetedModels = []; + let delay = 0; + const validModifiers = /* @__PURE__ */ new Map(); + validModifiers.set("delay", (modifier) => { + if (!isLoading) { + return; + } + delay = modifier.value ? Number.parseInt(modifier.value) : 200; + }); + validModifiers.set("action", (modifier) => { + if (!modifier.value) { + throw new Error( + `The "action" in data-loading must have an action name - e.g. action(foo). It's missing for "${directive.getString()}"` + ); + } + targetedActions.push(modifier.value); + }); + validModifiers.set("model", (modifier) => { + if (!modifier.value) { + throw new Error( + `The "model" in data-loading must have an action name - e.g. model(foo). It's missing for "${directive.getString()}"` + ); + } + targetedModels.push(modifier.value); + }); + directive.modifiers.forEach((modifier) => { + if (validModifiers.has(modifier.name)) { + const callable = validModifiers.get(modifier.name) ?? (() => { + }); + callable(modifier); + return; + } + throw new Error( + `Unknown modifier "${modifier.name}" used in data-loading="${directive.getString()}". Available modifiers are: ${Array.from(validModifiers.keys()).join(", ")}.` + ); + }); + if (isLoading && targetedActions.length > 0 && backendRequest && !backendRequest.containsOneOfActions(targetedActions)) { + return; + } + if (isLoading && targetedModels.length > 0 && backendRequest && !backendRequest.areAnyModelsUpdated(targetedModels)) { + return; + } + let loadingDirective; + switch (finalAction) { + case "show": + loadingDirective = () => this.showElement(element); + break; + case "hide": + loadingDirective = () => this.hideElement(element); + break; + case "addClass": + loadingDirective = () => this.addClass(element, directive.args); + break; + case "removeClass": + loadingDirective = () => this.removeClass(element, directive.args); + break; + case "addAttribute": + loadingDirective = () => this.addAttributes(element, directive.args); + break; + case "removeAttribute": + loadingDirective = () => this.removeAttributes(element, directive.args); + break; + default: + throw new Error(`Unknown data-loading action "${finalAction}"`); + } + if (delay) { + window.setTimeout(() => { + if (backendRequest && !backendRequest.isResolved) { + loadingDirective(); + } + }, delay); + return; + } + loadingDirective(); + } + getLoadingDirectives(component, element) { + const loadingDirectives = []; + let matchingElements = [...Array.from(element.querySelectorAll("[data-loading]"))]; + matchingElements = matchingElements.filter((elt) => elementBelongsToThisComponent(elt, component)); + if (element.hasAttribute("data-loading")) { + matchingElements = [element, ...matchingElements]; + } + matchingElements.forEach((element2) => { + if (!(element2 instanceof HTMLElement) && !(element2 instanceof SVGElement)) { + throw new Error("Invalid Element Type"); + } + const directives = parseDirectives(element2.dataset.loading || "show"); + loadingDirectives.push({ + element: element2, + directives + }); + }); + return loadingDirectives; + } + showElement(element) { + element.style.display = "revert"; + } + hideElement(element) { + element.style.display = "none"; + } + addClass(element, classes) { + element.classList.add(...combineSpacedArray(classes)); + } + removeClass(element, classes) { + element.classList.remove(...combineSpacedArray(classes)); + if (element.classList.length === 0) { + element.removeAttribute("class"); + } + } + addAttributes(element, attributes) { + attributes.forEach((attribute) => { + element.setAttribute(attribute, ""); + }); + } + removeAttributes(element, attributes) { + attributes.forEach((attribute) => { + element.removeAttribute(attribute); + }); + } +}; +var parseLoadingAction = (action, isLoading) => { + switch (action) { + case "show": + return isLoading ? "show" : "hide"; + case "hide": + return isLoading ? "hide" : "show"; + case "addClass": + return isLoading ? "addClass" : "removeClass"; + case "removeClass": + return isLoading ? "removeClass" : "addClass"; + case "addAttribute": + return isLoading ? "addAttribute" : "removeAttribute"; + case "removeAttribute": + return isLoading ? "removeAttribute" : "addAttribute"; + } + throw new Error(`Unknown data-loading action "${action}"`); +}; - oldNode.remove(); - ctx.callbacks.afterNodeRemoved(oldNode); - return null; - } else if (!isSoftMatch(oldNode, newContent)) { - if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode; - if (ctx.callbacks.beforeNodeAdded(newContent) === false) return oldNode; +// src/Component/plugins/PageUnloadingPlugin.ts +var PageUnloadingPlugin_default = class { + constructor() { + this.isConnected = false; + } + attachToComponent(component) { + component.on("render:started", (html, response, controls) => { + if (!this.isConnected) { + controls.shouldRender = false; + } + }); + component.on("connect", () => { + this.isConnected = true; + }); + component.on("disconnect", () => { + this.isConnected = false; + }); + } +}; - oldNode.parentElement.replaceChild(newContent, oldNode); - ctx.callbacks.afterNodeAdded(newContent); - ctx.callbacks.afterNodeRemoved(oldNode); - return newContent; - } else { - if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode; +// src/PollingDirector.ts +var PollingDirector_default = class { + constructor(component) { + this.isPollingActive = true; + this.pollingIntervals = []; + this.component = component; + } + addPoll(actionName, duration) { + this.polls.push({ actionName, duration }); + if (this.isPollingActive) { + this.initiatePoll(actionName, duration); + } + } + startAllPolling() { + if (this.isPollingActive) { + return; + } + this.isPollingActive = true; + this.polls.forEach(({ actionName, duration }) => { + this.initiatePoll(actionName, duration); + }); + } + stopAllPolling() { + this.isPollingActive = false; + this.pollingIntervals.forEach((interval) => { + clearInterval(interval); + }); + } + clearPolling() { + this.stopAllPolling(); + this.polls = []; + this.startAllPolling(); + } + initiatePoll(actionName, duration) { + let callback; + if (actionName === "$render") { + callback = () => { + this.component.render(); + }; + } else { + callback = () => { + this.component.action(actionName, {}, 0); + }; + } + const timer = window.setInterval(() => { + callback(); + }, duration); + this.pollingIntervals.push(timer); + } +}; - if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ; else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") { - handleHeadElement(newContent, oldNode, ctx); - } else { - syncNodeFrom(newContent, oldNode, ctx); - if (!ignoreValueOfActiveElement(oldNode, ctx)) { - morphChildren(newContent, oldNode, ctx); - } - } - ctx.callbacks.afterNodeMorphed(oldNode, newContent); - return oldNode; +// src/Component/plugins/PollingPlugin.ts +var PollingPlugin_default = class { + attachToComponent(component) { + this.element = component.element; + this.pollingDirector = new PollingDirector_default(component); + this.initializePolling(); + component.on("connect", () => { + this.pollingDirector.startAllPolling(); + }); + component.on("disconnect", () => { + this.pollingDirector.stopAllPolling(); + }); + component.on("render:finished", () => { + this.initializePolling(); + }); + } + addPoll(actionName, duration) { + this.pollingDirector.addPoll(actionName, duration); + } + clearPolling() { + this.pollingDirector.clearPolling(); + } + initializePolling() { + this.clearPolling(); + if (this.element.dataset.poll === void 0) { + return; + } + const rawPollConfig = this.element.dataset.poll; + const directives = parseDirectives(rawPollConfig || "$render"); + directives.forEach((directive) => { + let duration = 2e3; + directive.modifiers.forEach((modifier) => { + switch (modifier.name) { + case "delay": + if (modifier.value) { + duration = Number.parseInt(modifier.value); } + break; + default: + console.warn(`Unknown modifier "${modifier.name}" in data-poll "${rawPollConfig}".`); } + }); + this.addPoll(directive.action, duration); + }); + } +}; - /** - * This is the core algorithm for matching up children. The idea is to use id sets to try to match up - * nodes as faithfully as possible. We greedily match, which allows us to keep the algorithm fast, but - * by using id sets, we are able to better match up with content deeper in the DOM. - * - * Basic algorithm is, for each node in the new content: - * - * - if we have reached the end of the old parent, append the new content - * - if the new content has an id set match with the current insertion point, morph - * - search for an id set match - * - if id set match found, morph - * - otherwise search for a "soft" match - * - if a soft match is found, morph - * - otherwise, prepend the new node before the current insertion point - * - * The two search algorithms terminate if competing node matches appear to outweigh what can be achieved - * with the current node. See findIdSetMatch() and findSoftMatch() for details. - * - * @param {Element} newParent the parent element of the new content - * @param {Element } oldParent the old content that we are merging the new content into - * @param ctx the merge context - */ - function morphChildren(newParent, oldParent, ctx) { - - let nextNewChild = newParent.firstChild; - let insertionPoint = oldParent.firstChild; - let newChild; +// src/Component/plugins/SetValueOntoModelFieldsPlugin.ts +var SetValueOntoModelFieldsPlugin_default = class { + attachToComponent(component) { + this.synchronizeValueOfModelFields(component); + component.on("render:finished", () => { + this.synchronizeValueOfModelFields(component); + }); + } + /** + * Sets the "value" of all model fields to the component data. + * + * This is called when the component initializes and after re-render. + * Take the following element: + * + * + * + * This method will set the "value" of that element to the value of + * the "firstName" model. + */ + synchronizeValueOfModelFields(component) { + component.element.querySelectorAll("[data-model]").forEach((element) => { + if (!(element instanceof HTMLElement)) { + throw new Error("Invalid element using data-model."); + } + if (element instanceof HTMLFormElement) { + return; + } + if (!elementBelongsToThisComponent(element, component)) { + return; + } + const modelDirective = getModelDirectiveFromElement(element); + if (!modelDirective) { + return; + } + const modelName = modelDirective.action; + if (component.getUnsyncedModels().includes(modelName)) { + return; + } + if (component.valueStore.has(modelName)) { + setValueOnElement(element, component.valueStore.get(modelName)); + } + if (element instanceof HTMLSelectElement && !element.multiple) { + component.valueStore.set(modelName, getValueFromElement(element, component.valueStore)); + } + }); + } +}; - // run through all the new content - while (nextNewChild) { +// src/Component/plugins/ValidatedFieldsPlugin.ts +var ValidatedFieldsPlugin_default = class { + attachToComponent(component) { + component.on("model:set", (modelName) => { + this.handleModelSet(modelName, component.valueStore); + }); + } + handleModelSet(modelName, valueStore) { + if (valueStore.has("validatedFields")) { + const validatedFields = [...valueStore.get("validatedFields")]; + if (!validatedFields.includes(modelName)) { + validatedFields.push(modelName); + } + valueStore.set("validatedFields", validatedFields); + } + } +}; - newChild = nextNewChild; - nextNewChild = newChild.nextSibling; - - // if we are at the end of the exiting parent's children, just append - if (insertionPoint == null) { - if (ctx.callbacks.beforeNodeAdded(newChild) === false) return; - - oldParent.appendChild(newChild); - ctx.callbacks.afterNodeAdded(newChild); - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // if the current node has an id set match then morph - if (isIdSetMatch(newChild, insertionPoint, ctx)) { - morphOldNodeTo(insertionPoint, newChild, ctx); - insertionPoint = insertionPoint.nextSibling; - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // otherwise search forward in the existing old children for an id set match - let idSetMatch = findIdSetMatch(newParent, oldParent, newChild, insertionPoint, ctx); - - // if we found a potential match, remove the nodes until that point and morph - if (idSetMatch) { - insertionPoint = removeNodesBetween(insertionPoint, idSetMatch, ctx); - morphOldNodeTo(idSetMatch, newChild, ctx); - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // no id set match found, so scan forward for a soft match for the current node - let softMatch = findSoftMatch(newParent, oldParent, newChild, insertionPoint, ctx); - - // if we found a soft match for the current node, morph - if (softMatch) { - insertionPoint = removeNodesBetween(insertionPoint, softMatch, ctx); - morphOldNodeTo(softMatch, newChild, ctx); - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // abandon all hope of morphing, just insert the new child before the insertion point - // and move on - if (ctx.callbacks.beforeNodeAdded(newChild) === false) return; - - oldParent.insertBefore(newChild, insertionPoint); - ctx.callbacks.afterNodeAdded(newChild); - removeIdsFromConsideration(ctx, newChild); - } - - // remove any remaining old nodes that didn't match up with new content - while (insertionPoint !== null) { - - let tempNode = insertionPoint; - insertionPoint = insertionPoint.nextSibling; - removeNode(tempNode, ctx); - } - } - - //============================================================================= - // Attribute Syncing Code - //============================================================================= - - /** - * @param attr {String} the attribute to be mutated - * @param to {Element} the element that is going to be updated - * @param updateType {("update"|"remove")} - * @param ctx the merge context - * @returns {boolean} true if the attribute should be ignored, false otherwise - */ - function ignoreAttribute(attr, to, updateType, ctx) { - if(attr === 'value' && ctx.ignoreActiveValue && to === document.activeElement){ - return true; - } - return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false; - } - - /** - * syncs a given node with another node, copying over all attributes and - * inner element state from the 'from' node to the 'to' node - * - * @param {Element} from the element to copy attributes & state from - * @param {Element} to the element to copy attributes & state to - * @param ctx the merge context - */ - function syncNodeFrom(from, to, ctx) { - let type = from.nodeType; - - // if is an element type, sync the attributes from the - // new node into the new node - if (type === 1 /* element type */) { - const fromAttributes = from.attributes; - const toAttributes = to.attributes; - for (const fromAttribute of fromAttributes) { - if (ignoreAttribute(fromAttribute.name, to, 'update', ctx)) { - continue; - } - if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) { - to.setAttribute(fromAttribute.name, fromAttribute.value); - } - } - // iterate backwards to avoid skipping over items when a delete occurs - for (let i = toAttributes.length - 1; 0 <= i; i--) { - const toAttribute = toAttributes[i]; - if (ignoreAttribute(toAttribute.name, to, 'remove', ctx)) { - continue; - } - if (!from.hasAttribute(toAttribute.name)) { - to.removeAttribute(toAttribute.name); - } - } - } - - // sync text nodes - if (type === 8 /* comment */ || type === 3 /* text */) { - if (to.nodeValue !== from.nodeValue) { - to.nodeValue = from.nodeValue; - } - } - - if (!ignoreValueOfActiveElement(to, ctx)) { - // sync input values - syncInputValue(from, to, ctx); - } - } - - /** - * @param from {Element} element to sync the value from - * @param to {Element} element to sync the value to - * @param attributeName {String} the attribute name - * @param ctx the merge context - */ - function syncBooleanAttribute(from, to, attributeName, ctx) { - if (from[attributeName] !== to[attributeName]) { - let ignoreUpdate = ignoreAttribute(attributeName, to, 'update', ctx); - if (!ignoreUpdate) { - to[attributeName] = from[attributeName]; - } - if (from[attributeName]) { - if (!ignoreUpdate) { - to.setAttribute(attributeName, from[attributeName]); - } - } else { - if (!ignoreAttribute(attributeName, to, 'remove', ctx)) { - to.removeAttribute(attributeName); - } - } - } - } - - /** - * NB: many bothans died to bring us information: - * - * https://github.com/patrick-steele-idem/morphdom/blob/master/src/specialElHandlers.js - * https://github.com/choojs/nanomorph/blob/master/lib/morph.jsL113 - * - * @param from {Element} the element to sync the input value from - * @param to {Element} the element to sync the input value to - * @param ctx the merge context - */ - function syncInputValue(from, to, ctx) { - if (from instanceof HTMLInputElement && - to instanceof HTMLInputElement && - from.type !== 'file') { - - let fromValue = from.value; - let toValue = to.value; - - // sync boolean attributes - syncBooleanAttribute(from, to, 'checked', ctx); - syncBooleanAttribute(from, to, 'disabled', ctx); - - if (!from.hasAttribute('value')) { - if (!ignoreAttribute('value', to, 'remove', ctx)) { - to.value = ''; - to.removeAttribute('value'); - } - } else if (fromValue !== toValue) { - if (!ignoreAttribute('value', to, 'update', ctx)) { - to.setAttribute('value', fromValue); - to.value = fromValue; - } - } - } else if (from instanceof HTMLOptionElement) { - syncBooleanAttribute(from, to, 'selected', ctx); - } else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) { - let fromValue = from.value; - let toValue = to.value; - if (ignoreAttribute('value', to, 'update', ctx)) { - return; - } - if (fromValue !== toValue) { - to.value = fromValue; - } - if (to.firstChild && to.firstChild.nodeValue !== fromValue) { - to.firstChild.nodeValue = fromValue; - } - } - } - - //============================================================================= - // the HEAD tag can be handled specially, either w/ a 'merge' or 'append' style - //============================================================================= - function handleHeadElement(newHeadTag, currentHead, ctx) { - - let added = []; - let removed = []; - let preserved = []; - let nodesToAppend = []; - - let headMergeStyle = ctx.head.style; - - // put all new head elements into a Map, by their outerHTML - let srcToNewHeadNodes = new Map(); - for (const newHeadChild of newHeadTag.children) { - srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild); - } - - // for each elt in the current head - for (const currentHeadElt of currentHead.children) { - - // If the current head element is in the map - let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML); - let isReAppended = ctx.head.shouldReAppend(currentHeadElt); - let isPreserved = ctx.head.shouldPreserve(currentHeadElt); - if (inNewContent || isPreserved) { - if (isReAppended) { - // remove the current version and let the new version replace it and re-execute - removed.push(currentHeadElt); - } else { - // this element already exists and should not be re-appended, so remove it from - // the new content map, preserving it in the DOM - srcToNewHeadNodes.delete(currentHeadElt.outerHTML); - preserved.push(currentHeadElt); - } - } else { - if (headMergeStyle === "append") { - // we are appending and this existing element is not new content - // so if and only if it is marked for re-append do we do anything - if (isReAppended) { - removed.push(currentHeadElt); - nodesToAppend.push(currentHeadElt); - } - } else { - // if this is a merge, we remove this content since it is not in the new head - if (ctx.head.shouldRemove(currentHeadElt) !== false) { - removed.push(currentHeadElt); - } - } - } - } - - // Push the remaining new head elements in the Map into the - // nodes to append to the head tag - nodesToAppend.push(...srcToNewHeadNodes.values()); - - let promises = []; - for (const newNode of nodesToAppend) { - let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild; - if (ctx.callbacks.beforeNodeAdded(newElt) !== false) { - if (newElt.href || newElt.src) { - let resolve = null; - let promise = new Promise(function (_resolve) { - resolve = _resolve; - }); - newElt.addEventListener('load', function () { - resolve(); - }); - promises.push(promise); - } - currentHead.appendChild(newElt); - ctx.callbacks.afterNodeAdded(newElt); - added.push(newElt); - } - } - - // remove all removed elements, after we have appended the new elements to avoid - // additional network requests for things like style sheets - for (const removedElement of removed) { - if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) { - currentHead.removeChild(removedElement); - ctx.callbacks.afterNodeRemoved(removedElement); - } - } - - ctx.head.afterHeadMorphed(currentHead, {added: added, kept: preserved, removed: removed}); - return promises; - } - - function noOp() { - } - - /* - Deep merges the config object and the Idiomoroph.defaults object to - produce a final configuration object - */ - function mergeDefaults(config) { - let finalConfig = {}; - // copy top level stuff into final config - Object.assign(finalConfig, defaults); - Object.assign(finalConfig, config); - - // copy callbacks into final config (do this to deep merge the callbacks) - finalConfig.callbacks = {}; - Object.assign(finalConfig.callbacks, defaults.callbacks); - Object.assign(finalConfig.callbacks, config.callbacks); - - // copy head config into final config (do this to deep merge the head) - finalConfig.head = {}; - Object.assign(finalConfig.head, defaults.head); - Object.assign(finalConfig.head, config.head); - return finalConfig; - } - - function createMorphContext(oldNode, newContent, config) { - config = mergeDefaults(config); - return { - target: oldNode, - newContent: newContent, - config: config, - morphStyle: config.morphStyle, - ignoreActive: config.ignoreActive, - ignoreActiveValue: config.ignoreActiveValue, - idMap: createIdMap(oldNode, newContent), - deadIds: new Set(), - callbacks: config.callbacks, - head: config.head - } - } - - function isIdSetMatch(node1, node2, ctx) { - if (node1 == null || node2 == null) { - return false; - } - if (node1.nodeType === node2.nodeType && node1.tagName === node2.tagName) { - if (node1.id !== "" && node1.id === node2.id) { - return true; - } else { - return getIdIntersectionCount(ctx, node1, node2) > 0; - } - } - return false; - } - - function isSoftMatch(node1, node2) { - if (node1 == null || node2 == null) { - return false; - } - return node1.nodeType === node2.nodeType && node1.tagName === node2.tagName - } - - function removeNodesBetween(startInclusive, endExclusive, ctx) { - while (startInclusive !== endExclusive) { - let tempNode = startInclusive; - startInclusive = startInclusive.nextSibling; - removeNode(tempNode, ctx); - } - removeIdsFromConsideration(ctx, endExclusive); - return endExclusive.nextSibling; - } - - //============================================================================= - // Scans forward from the insertionPoint in the old parent looking for a potential id match - // for the newChild. We stop if we find a potential id match for the new child OR - // if the number of potential id matches we are discarding is greater than the - // potential id matches for the new child - //============================================================================= - function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) { - - // max id matches we are willing to discard in our search - let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent); - - let potentialMatch = null; - - // only search forward if there is a possibility of an id match - if (newChildPotentialIdCount > 0) { - let potentialMatch = insertionPoint; - // if there is a possibility of an id match, scan forward - // keep track of the potential id match count we are discarding (the - // newChildPotentialIdCount must be greater than this to make it likely - // worth it) - let otherMatchCount = 0; - while (potentialMatch != null) { - - // If we have an id match, return the current potential match - if (isIdSetMatch(newChild, potentialMatch, ctx)) { - return potentialMatch; - } - - // computer the other potential matches of this new content - otherMatchCount += getIdIntersectionCount(ctx, potentialMatch, newContent); - if (otherMatchCount > newChildPotentialIdCount) { - // if we have more potential id matches in _other_ content, we - // do not have a good candidate for an id match, so return null - return null; - } - - // advanced to the next old content child - potentialMatch = potentialMatch.nextSibling; - } - } - return potentialMatch; - } - - //============================================================================= - // Scans forward from the insertionPoint in the old parent looking for a potential soft match - // for the newChild. We stop if we find a potential soft match for the new child OR - // if we find a potential id match in the old parents children OR if we find two - // potential soft matches for the next two pieces of new content - //============================================================================= - function findSoftMatch(newContent, oldParent, newChild, insertionPoint, ctx) { - - let potentialSoftMatch = insertionPoint; - let nextSibling = newChild.nextSibling; - let siblingSoftMatchCount = 0; - - while (potentialSoftMatch != null) { - - if (getIdIntersectionCount(ctx, potentialSoftMatch, newContent) > 0) { - // the current potential soft match has a potential id set match with the remaining new - // content so bail out of looking - return null; - } - - // if we have a soft match with the current node, return it - if (isSoftMatch(newChild, potentialSoftMatch)) { - return potentialSoftMatch; - } - - if (isSoftMatch(nextSibling, potentialSoftMatch)) { - // the next new node has a soft match with this node, so - // increment the count of future soft matches - siblingSoftMatchCount++; - nextSibling = nextSibling.nextSibling; - - // If there are two future soft matches, bail to allow the siblings to soft match - // so that we don't consume future soft matches for the sake of the current node - if (siblingSoftMatchCount >= 2) { - return null; - } - } - - // advanced to the next old content child - potentialSoftMatch = potentialSoftMatch.nextSibling; - } - - return potentialSoftMatch; - } - - function parseContent(newContent) { - let parser = new DOMParser(); - - // remove svgs to avoid false-positive matches on head, etc. - let contentWithSvgsRemoved = newContent.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - - // if the newContent contains a html, head or body tag, we can simply parse it w/o wrapping - if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) { - let content = parser.parseFromString(newContent, "text/html"); - // if it is a full HTML document, return the document itself as the parent container - if (contentWithSvgsRemoved.match(/<\/html>/)) { - content.generatedByIdiomorph = true; - return content; - } else { - // otherwise return the html element as the parent container - let htmlElement = content.firstChild; - if (htmlElement) { - htmlElement.generatedByIdiomorph = true; - return htmlElement; - } else { - return null; - } - } - } else { - // if it is partial HTML, wrap it in a template tag to provide a parent element and also to help - // deal with touchy tags like tr, tbody, etc. - let responseDoc = parser.parseFromString("" + newContent + "", "text/html"); - let content = responseDoc.body.querySelector('template').content; - content.generatedByIdiomorph = true; - return content - } - } - - function normalizeContent(newContent) { - if (newContent == null) { - // noinspection UnnecessaryLocalVariableJS - const dummyParent = document.createElement('div'); - return dummyParent; - } else if (newContent.generatedByIdiomorph) { - // the template tag created by idiomorph parsing can serve as a dummy parent - return newContent; - } else if (newContent instanceof Node) { - // a single node is added as a child to a dummy parent - const dummyParent = document.createElement('div'); - dummyParent.append(newContent); - return dummyParent; - } else { - // all nodes in the array or HTMLElement collection are consolidated under - // a single dummy parent element - const dummyParent = document.createElement('div'); - for (const elt of [...newContent]) { - dummyParent.append(elt); - } - return dummyParent; - } - } - - function insertSiblings(previousSibling, morphedNode, nextSibling) { - let stack = []; - let added = []; - while (previousSibling != null) { - stack.push(previousSibling); - previousSibling = previousSibling.previousSibling; - } - while (stack.length > 0) { - let node = stack.pop(); - added.push(node); // push added preceding siblings on in order and insert - morphedNode.parentElement.insertBefore(node, morphedNode); - } - added.push(morphedNode); - while (nextSibling != null) { - stack.push(nextSibling); - added.push(nextSibling); // here we are going in order, so push on as we scan, rather than add - nextSibling = nextSibling.nextSibling; - } - while (stack.length > 0) { - morphedNode.parentElement.insertBefore(stack.pop(), morphedNode.nextSibling); - } - return added; - } - - function findBestNodeMatch(newContent, oldNode, ctx) { - let currentElement; - currentElement = newContent.firstChild; - let bestElement = currentElement; - let score = 0; - while (currentElement) { - let newScore = scoreElement(currentElement, oldNode, ctx); - if (newScore > score) { - bestElement = currentElement; - score = newScore; - } - currentElement = currentElement.nextSibling; - } - return bestElement; - } - - function scoreElement(node1, node2, ctx) { - if (isSoftMatch(node1, node2)) { - return .5 + getIdIntersectionCount(ctx, node1, node2); - } - return 0; - } - - function removeNode(tempNode, ctx) { - removeIdsFromConsideration(ctx, tempNode); - if (ctx.callbacks.beforeNodeRemoved(tempNode) === false) return; - - tempNode.remove(); - ctx.callbacks.afterNodeRemoved(tempNode); - } - - //============================================================================= - // ID Set Functions - //============================================================================= - - function isIdInConsideration(ctx, id) { - return !ctx.deadIds.has(id); - } - - function idIsWithinNode(ctx, id, targetNode) { - let idSet = ctx.idMap.get(targetNode) || EMPTY_SET; - return idSet.has(id); - } - - function removeIdsFromConsideration(ctx, node) { - let idSet = ctx.idMap.get(node) || EMPTY_SET; - for (const id of idSet) { - ctx.deadIds.add(id); - } - } - - function getIdIntersectionCount(ctx, node1, node2) { - let sourceSet = ctx.idMap.get(node1) || EMPTY_SET; - let matchCount = 0; - for (const id of sourceSet) { - // a potential match is an id in the source and potentialIdsSet, but - // that has not already been merged into the DOM - if (isIdInConsideration(ctx, id) && idIsWithinNode(ctx, id, node2)) { - ++matchCount; - } - } - return matchCount; - } - - /** - * A bottom up algorithm that finds all elements with ids inside of the node - * argument and populates id sets for those nodes and all their parents, generating - * a set of ids contained within all nodes for the entire hierarchy in the DOM - * - * @param node {Element} - * @param {Map>} idMap - */ - function populateIdMapForNode(node, idMap) { - let nodeParent = node.parentElement; - // find all elements with an id property - let idElements = node.querySelectorAll('[id]'); - for (const elt of idElements) { - let current = elt; - // walk up the parent hierarchy of that element, adding the id - // of element to the parent's id set - while (current !== nodeParent && current != null) { - let idSet = idMap.get(current); - // if the id set doesn't exist, create it and insert it in the map - if (idSet == null) { - idSet = new Set(); - idMap.set(current, idSet); - } - idSet.add(elt.id); - current = current.parentElement; - } - } - } - - /** - * This function computes a map of nodes to all ids contained within that node (inclusive of the - * node). This map can be used to ask if two nodes have intersecting sets of ids, which allows - * for a looser definition of "matching" than tradition id matching, and allows child nodes - * to contribute to a parent nodes matching. - * - * @param {Element} oldContent the old content that will be morphed - * @param {Element} newContent the new content to morph to - * @returns {Map>} a map of nodes to id sets for the - */ - function createIdMap(oldContent, newContent) { - let idMap = new Map(); - populateIdMapForNode(oldContent, idMap); - populateIdMapForNode(newContent, idMap); - return idMap; - } - - //============================================================================= - // This is what ends up becoming the Idiomorph global object - //============================================================================= - return { - morph, - defaults - } - })(); - -function normalizeAttributesForComparison(element) { - const isFileInput = element instanceof HTMLInputElement && element.type === 'file'; - if (!isFileInput) { - if ('value' in element) { - element.setAttribute('value', element.value); - } - else if (element.hasAttribute('value')) { - element.setAttribute('value', ''); - } - } - Array.from(element.children).forEach((child) => { - normalizeAttributesForComparison(child); - }); -} - -const syncAttributes = (fromEl, toEl) => { - for (let i = 0; i < fromEl.attributes.length; i++) { - const attr = fromEl.attributes[i]; - toEl.setAttribute(attr.name, attr.value); - } -}; -function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements, getElementValue, externalMutationTracker) { - const originalElementIdsToSwapAfter = []; - const originalElementsToPreserve = new Map(); - const markElementAsNeedingPostMorphSwap = (id, replaceWithClone) => { - const oldElement = originalElementsToPreserve.get(id); - if (!(oldElement instanceof HTMLElement)) { - throw new Error(`Original element with id ${id} not found`); - } - originalElementIdsToSwapAfter.push(id); - if (!replaceWithClone) { - return null; - } - const clonedOldElement = cloneHTMLElement(oldElement); - oldElement.replaceWith(clonedOldElement); - return clonedOldElement; - }; - rootToElement.querySelectorAll('[data-live-preserve]').forEach((newElement) => { - const id = newElement.id; - if (!id) { - throw new Error('The data-live-preserve attribute requires an id attribute to be set on the element'); - } - const oldElement = rootFromElement.querySelector(`#${id}`); - if (!(oldElement instanceof HTMLElement)) { - throw new Error(`The element with id "${id}" was not found in the original HTML`); - } - newElement.removeAttribute('data-live-preserve'); - originalElementsToPreserve.set(id, oldElement); - syncAttributes(newElement, oldElement); - }); - Idiomorph.morph(rootFromElement, rootToElement, { - callbacks: { - beforeNodeMorphed: (fromEl, toEl) => { - if (!(fromEl instanceof Element) || !(toEl instanceof Element)) { - return true; - } - if (fromEl === rootFromElement) { - return true; - } - if (fromEl.id && originalElementsToPreserve.has(fromEl.id)) { - if (fromEl.id === toEl.id) { - return false; - } - const clonedFromEl = markElementAsNeedingPostMorphSwap(fromEl.id, true); - if (!clonedFromEl) { - throw new Error('missing clone'); - } - Idiomorph.morph(clonedFromEl, toEl); - return false; - } - if (fromEl instanceof HTMLElement && toEl instanceof HTMLElement) { - if (typeof fromEl.__x !== 'undefined') { - if (!window.Alpine) { - throw new Error('Unable to access Alpine.js though the global window.Alpine variable. Please make sure Alpine.js is loaded before Symfony UX LiveComponent.'); - } - if (typeof window.Alpine.morph !== 'function') { - throw new Error('Unable to access Alpine.js morph function. Please make sure the Alpine.js Morph plugin is installed and loaded, see https://alpinejs.dev/plugins/morph for more information.'); - } - window.Alpine.morph(fromEl.__x, toEl); - } - if (externalMutationTracker.wasElementAdded(fromEl)) { - fromEl.insertAdjacentElement('afterend', toEl); - return false; - } - if (modifiedFieldElements.includes(fromEl)) { - setValueOnElement(toEl, getElementValue(fromEl)); - } - if (fromEl === document.activeElement && - fromEl !== document.body && - null !== getModelDirectiveFromElement(fromEl, false)) { - setValueOnElement(toEl, getElementValue(fromEl)); - } - const elementChanges = externalMutationTracker.getChangedElement(fromEl); - if (elementChanges) { - elementChanges.applyToElement(toEl); - } - if (fromEl.nodeName.toUpperCase() !== 'OPTION' && fromEl.isEqualNode(toEl)) { - const normalizedFromEl = cloneHTMLElement(fromEl); - normalizeAttributesForComparison(normalizedFromEl); - const normalizedToEl = cloneHTMLElement(toEl); - normalizeAttributesForComparison(normalizedToEl); - if (normalizedFromEl.isEqualNode(normalizedToEl)) { - return false; - } - } - } - if (fromEl.hasAttribute('data-skip-morph') || (fromEl.id && fromEl.id !== toEl.id)) { - fromEl.innerHTML = toEl.innerHTML; - return true; - } - if (fromEl.parentElement?.hasAttribute('data-skip-morph')) { - return false; - } - return !fromEl.hasAttribute('data-live-ignore'); - }, - beforeNodeRemoved(node) { - if (!(node instanceof HTMLElement)) { - return true; - } - if (node.id && originalElementsToPreserve.has(node.id)) { - markElementAsNeedingPostMorphSwap(node.id, false); - return true; - } - if (externalMutationTracker.wasElementAdded(node)) { - return false; - } - return !node.hasAttribute('data-live-ignore'); - }, - }, - }); - originalElementIdsToSwapAfter.forEach((id) => { - const newElement = rootFromElement.querySelector(`#${id}`); - const originalElement = originalElementsToPreserve.get(id); - if (!(newElement instanceof HTMLElement) || !(originalElement instanceof HTMLElement)) { - throw new Error('Missing elements.'); - } - newElement.replaceWith(originalElement); - }); -} - -class ChangingItemsTracker { - constructor() { - this.changedItems = new Map(); - this.removedItems = new Map(); - } - setItem(itemName, newValue, previousValue) { - if (this.removedItems.has(itemName)) { - const removedRecord = this.removedItems.get(itemName); - this.removedItems.delete(itemName); - if (removedRecord.original === newValue) { - return; - } - } - if (this.changedItems.has(itemName)) { - const originalRecord = this.changedItems.get(itemName); - if (originalRecord.original === newValue) { - this.changedItems.delete(itemName); - return; - } - this.changedItems.set(itemName, { original: originalRecord.original, new: newValue }); - return; - } - this.changedItems.set(itemName, { original: previousValue, new: newValue }); - } - removeItem(itemName, currentValue) { - let trueOriginalValue = currentValue; - if (this.changedItems.has(itemName)) { - const originalRecord = this.changedItems.get(itemName); - trueOriginalValue = originalRecord.original; - this.changedItems.delete(itemName); - if (trueOriginalValue === null) { - return; - } - } - if (!this.removedItems.has(itemName)) { - this.removedItems.set(itemName, { original: trueOriginalValue }); - } - } - getChangedItems() { - return Array.from(this.changedItems, ([name, { new: value }]) => ({ name, value })); - } - getRemovedItems() { - return Array.from(this.removedItems.keys()); - } - isEmpty() { - return this.changedItems.size === 0 && this.removedItems.size === 0; - } -} - -class ElementChanges { - constructor() { - this.addedClasses = new Set(); - this.removedClasses = new Set(); - this.styleChanges = new ChangingItemsTracker(); - this.attributeChanges = new ChangingItemsTracker(); - } - addClass(className) { - if (!this.removedClasses.delete(className)) { - this.addedClasses.add(className); - } - } - removeClass(className) { - if (!this.addedClasses.delete(className)) { - this.removedClasses.add(className); - } - } - addStyle(styleName, newValue, originalValue) { - this.styleChanges.setItem(styleName, newValue, originalValue); - } - removeStyle(styleName, originalValue) { - this.styleChanges.removeItem(styleName, originalValue); - } - addAttribute(attributeName, newValue, originalValue) { - this.attributeChanges.setItem(attributeName, newValue, originalValue); - } - removeAttribute(attributeName, originalValue) { - this.attributeChanges.removeItem(attributeName, originalValue); - } - getAddedClasses() { - return [...this.addedClasses]; - } - getRemovedClasses() { - return [...this.removedClasses]; - } - getChangedStyles() { - return this.styleChanges.getChangedItems(); - } - getRemovedStyles() { - return this.styleChanges.getRemovedItems(); - } - getChangedAttributes() { - return this.attributeChanges.getChangedItems(); - } - getRemovedAttributes() { - return this.attributeChanges.getRemovedItems(); - } - applyToElement(element) { - element.classList.add(...this.addedClasses); - element.classList.remove(...this.removedClasses); - this.styleChanges.getChangedItems().forEach((change) => { - element.style.setProperty(change.name, change.value); - return; - }); - this.styleChanges.getRemovedItems().forEach((styleName) => { - element.style.removeProperty(styleName); - }); - this.attributeChanges.getChangedItems().forEach((change) => { - element.setAttribute(change.name, change.value); - }); - this.attributeChanges.getRemovedItems().forEach((attributeName) => { - element.removeAttribute(attributeName); - }); - } - isEmpty() { - return (this.addedClasses.size === 0 && - this.removedClasses.size === 0 && - this.styleChanges.isEmpty() && - this.attributeChanges.isEmpty()); - } -} - -class ExternalMutationTracker { - constructor(element, shouldTrackChangeCallback) { - this.changedElements = new WeakMap(); - this.changedElementsCount = 0; - this.addedElements = []; - this.removedElements = []; - this.isStarted = false; - this.element = element; - this.shouldTrackChangeCallback = shouldTrackChangeCallback; - this.mutationObserver = new MutationObserver(this.onMutations.bind(this)); - } - start() { - if (this.isStarted) { - return; - } - this.mutationObserver.observe(this.element, { - childList: true, - subtree: true, - attributes: true, - attributeOldValue: true, - }); - this.isStarted = true; - } - stop() { - if (this.isStarted) { - this.mutationObserver.disconnect(); - this.isStarted = false; - } - } - getChangedElement(element) { - return this.changedElements.has(element) ? this.changedElements.get(element) : null; - } - getAddedElements() { - return this.addedElements; - } - wasElementAdded(element) { - return this.addedElements.includes(element); - } - handlePendingChanges() { - this.onMutations(this.mutationObserver.takeRecords()); - } - onMutations(mutations) { - const handledAttributeMutations = new WeakMap(); - for (const mutation of mutations) { - const element = mutation.target; - if (!this.shouldTrackChangeCallback(element)) { - continue; - } - if (this.isElementAddedByTranslation(element)) { - continue; - } - let isChangeInAddedElement = false; - for (const addedElement of this.addedElements) { - if (addedElement.contains(element)) { - isChangeInAddedElement = true; - break; - } - } - if (isChangeInAddedElement) { - continue; - } - switch (mutation.type) { - case 'childList': - this.handleChildListMutation(mutation); - break; - case 'attributes': - if (!handledAttributeMutations.has(element)) { - handledAttributeMutations.set(element, []); - } - if (!handledAttributeMutations.get(element).includes(mutation.attributeName)) { - this.handleAttributeMutation(mutation); - handledAttributeMutations.set(element, [ - ...handledAttributeMutations.get(element), - mutation.attributeName, - ]); - } - break; - } - } - } - handleChildListMutation(mutation) { - mutation.addedNodes.forEach((node) => { - if (!(node instanceof Element)) { - return; - } - if (this.removedElements.includes(node)) { - this.removedElements.splice(this.removedElements.indexOf(node), 1); - return; - } - if (this.isElementAddedByTranslation(node)) { - return; - } - this.addedElements.push(node); - }); - mutation.removedNodes.forEach((node) => { - if (!(node instanceof Element)) { - return; - } - if (this.addedElements.includes(node)) { - this.addedElements.splice(this.addedElements.indexOf(node), 1); - return; - } - this.removedElements.push(node); - }); - } - handleAttributeMutation(mutation) { - const element = mutation.target; - if (!this.changedElements.has(element)) { - this.changedElements.set(element, new ElementChanges()); - this.changedElementsCount++; - } - const changedElement = this.changedElements.get(element); - switch (mutation.attributeName) { - case 'class': - this.handleClassAttributeMutation(mutation, changedElement); - break; - case 'style': - this.handleStyleAttributeMutation(mutation, changedElement); - break; - default: - this.handleGenericAttributeMutation(mutation, changedElement); - } - if (changedElement.isEmpty()) { - this.changedElements.delete(element); - this.changedElementsCount--; - } - } - handleClassAttributeMutation(mutation, elementChanges) { - const element = mutation.target; - const previousValue = mutation.oldValue || ''; - const previousValues = previousValue.match(/(\S+)/gu) || []; - const newValues = [].slice.call(element.classList); - const addedValues = newValues.filter((value) => !previousValues.includes(value)); - const removedValues = previousValues.filter((value) => !newValues.includes(value)); - addedValues.forEach((value) => { - elementChanges.addClass(value); - }); - removedValues.forEach((value) => { - elementChanges.removeClass(value); - }); - } - handleStyleAttributeMutation(mutation, elementChanges) { - const element = mutation.target; - const previousValue = mutation.oldValue || ''; - const previousStyles = this.extractStyles(previousValue); - const newValue = element.getAttribute('style') || ''; - const newStyles = this.extractStyles(newValue); - const addedOrChangedStyles = Object.keys(newStyles).filter((key) => previousStyles[key] === undefined || previousStyles[key] !== newStyles[key]); - const removedStyles = Object.keys(previousStyles).filter((key) => !newStyles[key]); - addedOrChangedStyles.forEach((style) => { - elementChanges.addStyle(style, newStyles[style], previousStyles[style] === undefined ? null : previousStyles[style]); - }); - removedStyles.forEach((style) => { - elementChanges.removeStyle(style, previousStyles[style]); - }); - } - handleGenericAttributeMutation(mutation, elementChanges) { - const attributeName = mutation.attributeName; - const element = mutation.target; - let oldValue = mutation.oldValue; - let newValue = element.getAttribute(attributeName); - if (oldValue === attributeName) { - oldValue = ''; - } - if (newValue === attributeName) { - newValue = ''; - } - if (!element.hasAttribute(attributeName)) { - if (oldValue === null) { - return; - } - elementChanges.removeAttribute(attributeName, mutation.oldValue); - return; - } - if (newValue === oldValue) { - return; - } - elementChanges.addAttribute(attributeName, element.getAttribute(attributeName), mutation.oldValue); - } - extractStyles(styles) { - const styleObject = {}; - styles.split(';').forEach((style) => { - const parts = style.split(':'); - if (parts.length === 1) { - return; - } - const property = parts[0].trim(); - styleObject[property] = parts.slice(1).join(':').trim(); - }); - return styleObject; - } - isElementAddedByTranslation(element) { - return element.tagName === 'FONT' && element.getAttribute('style') === 'vertical-align: inherit;'; - } -} - -class UnsyncedInputsTracker { - constructor(component, modelElementResolver) { - this.elementEventListeners = [ - { event: 'input', callback: (event) => this.handleInputEvent(event) }, - ]; - this.component = component; - this.modelElementResolver = modelElementResolver; - this.unsyncedInputs = new UnsyncedInputContainer(); - } - activate() { - this.elementEventListeners.forEach(({ event, callback }) => { - this.component.element.addEventListener(event, callback); - }); - } - deactivate() { - this.elementEventListeners.forEach(({ event, callback }) => { - this.component.element.removeEventListener(event, callback); - }); - } - markModelAsSynced(modelName) { - this.unsyncedInputs.markModelAsSynced(modelName); - } - handleInputEvent(event) { - const target = event.target; - if (!target) { - return; - } - this.updateModelFromElement(target); - } - updateModelFromElement(element) { - if (!elementBelongsToThisComponent(element, this.component)) { - return; - } - if (!(element instanceof HTMLElement)) { - throw new Error('Could not update model for non HTMLElement'); - } - const modelName = this.modelElementResolver.getModelName(element); - this.unsyncedInputs.add(element, modelName); - } - getUnsyncedInputs() { - return this.unsyncedInputs.allUnsyncedInputs(); - } - getUnsyncedModels() { - return Array.from(this.unsyncedInputs.getUnsyncedModelNames()); - } - resetUnsyncedFields() { - this.unsyncedInputs.resetUnsyncedFields(); - } -} -class UnsyncedInputContainer { - constructor() { - this.unsyncedNonModelFields = []; - this.unsyncedModelNames = []; - this.unsyncedModelFields = new Map(); - } - add(element, modelName = null) { - if (modelName) { - this.unsyncedModelFields.set(modelName, element); - if (!this.unsyncedModelNames.includes(modelName)) { - this.unsyncedModelNames.push(modelName); - } - return; - } - this.unsyncedNonModelFields.push(element); - } - resetUnsyncedFields() { - this.unsyncedModelFields.forEach((value, key) => { - if (!this.unsyncedModelNames.includes(key)) { - this.unsyncedModelFields.delete(key); - } - }); - } - allUnsyncedInputs() { - return [...this.unsyncedNonModelFields, ...this.unsyncedModelFields.values()]; - } - markModelAsSynced(modelName) { - const index = this.unsyncedModelNames.indexOf(modelName); - if (index !== -1) { - this.unsyncedModelNames.splice(index, 1); - } - } - getUnsyncedModelNames() { - return this.unsyncedModelNames; - } -} - -function getDeepData(data, propertyPath) { - const { currentLevelData, finalKey } = parseDeepData(data, propertyPath); - if (currentLevelData === undefined) { - return undefined; - } - return currentLevelData[finalKey]; -} -const parseDeepData = (data, propertyPath) => { - const finalData = JSON.parse(JSON.stringify(data)); - let currentLevelData = finalData; - const parts = propertyPath.split('.'); - for (let i = 0; i < parts.length - 1; i++) { - currentLevelData = currentLevelData[parts[i]]; - } - const finalKey = parts[parts.length - 1]; - return { - currentLevelData, - finalData, - finalKey, - parts, - }; -}; - -class ValueStore { - constructor(props) { - this.props = {}; - this.dirtyProps = {}; - this.pendingProps = {}; - this.updatedPropsFromParent = {}; - this.props = props; - } - get(name) { - const normalizedName = normalizeModelName(name); - if (this.dirtyProps[normalizedName] !== undefined) { - return this.dirtyProps[normalizedName]; - } - if (this.pendingProps[normalizedName] !== undefined) { - return this.pendingProps[normalizedName]; - } - if (this.props[normalizedName] !== undefined) { - return this.props[normalizedName]; - } - return getDeepData(this.props, normalizedName); - } - has(name) { - return this.get(name) !== undefined; - } - set(name, value) { - const normalizedName = normalizeModelName(name); - if (this.get(normalizedName) === value) { - return false; - } - this.dirtyProps[normalizedName] = value; - return true; - } - getOriginalProps() { - return { ...this.props }; - } - getDirtyProps() { - return { ...this.dirtyProps }; - } - getUpdatedPropsFromParent() { - return { ...this.updatedPropsFromParent }; - } - flushDirtyPropsToPending() { - this.pendingProps = { ...this.dirtyProps }; - this.dirtyProps = {}; - } - reinitializeAllProps(props) { - this.props = props; - this.updatedPropsFromParent = {}; - this.pendingProps = {}; - } - pushPendingPropsBackToDirty() { - this.dirtyProps = { ...this.pendingProps, ...this.dirtyProps }; - this.pendingProps = {}; - } - storeNewPropsFromParent(props) { - let changed = false; - for (const [key, value] of Object.entries(props)) { - const currentValue = this.get(key); - if (currentValue !== value) { - changed = true; - } - } - if (changed) { - this.updatedPropsFromParent = props; - } - return changed; - } -} - -class Component { - constructor(element, name, props, listeners, id, backend, elementDriver) { - this.fingerprint = ''; - this.defaultDebounce = 150; - this.backendRequest = null; - this.pendingActions = []; - this.pendingFiles = {}; - this.isRequestPending = false; - this.requestDebounceTimeout = null; - this.element = element; - this.name = name; - this.backend = backend; - this.elementDriver = elementDriver; - this.id = id; - this.listeners = new Map(); - listeners.forEach((listener) => { - if (!this.listeners.has(listener.event)) { - this.listeners.set(listener.event, []); - } - this.listeners.get(listener.event)?.push(listener.action); - }); - this.valueStore = new ValueStore(props); - this.unsyncedInputsTracker = new UnsyncedInputsTracker(this, elementDriver); - this.hooks = new HookManager(); - this.resetPromise(); - this.externalMutationTracker = new ExternalMutationTracker(this.element, (element) => elementBelongsToThisComponent(element, this)); - this.externalMutationTracker.start(); - } - addPlugin(plugin) { - plugin.attachToComponent(this); - } - connect() { - registerComponent(this); - this.hooks.triggerHook('connect', this); - this.unsyncedInputsTracker.activate(); - this.externalMutationTracker.start(); - } - disconnect() { - unregisterComponent(this); - this.hooks.triggerHook('disconnect', this); - this.clearRequestDebounceTimeout(); - this.unsyncedInputsTracker.deactivate(); - this.externalMutationTracker.stop(); - } - on(hookName, callback) { - this.hooks.register(hookName, callback); - } - off(hookName, callback) { - this.hooks.unregister(hookName, callback); - } - set(model, value, reRender = false, debounce = false) { - const promise = this.nextRequestPromise; - const modelName = normalizeModelName(model); - if (!this.valueStore.has(modelName)) { - throw new Error(`Invalid model name "${model}".`); - } - const isChanged = this.valueStore.set(modelName, value); - this.hooks.triggerHook('model:set', model, value, this); - this.unsyncedInputsTracker.markModelAsSynced(modelName); - if (reRender && isChanged) { - this.debouncedStartRequest(debounce); - } - return promise; - } - getData(model) { - const modelName = normalizeModelName(model); - if (!this.valueStore.has(modelName)) { - throw new Error(`Invalid model "${model}".`); - } - return this.valueStore.get(modelName); - } - action(name, args = {}, debounce = false) { - const promise = this.nextRequestPromise; - this.pendingActions.push({ - name, - args, - }); - this.debouncedStartRequest(debounce); - return promise; - } - files(key, input) { - this.pendingFiles[key] = input; - } - render() { - const promise = this.nextRequestPromise; - this.tryStartingRequest(); - return promise; - } - getUnsyncedModels() { - return this.unsyncedInputsTracker.getUnsyncedModels(); - } - emit(name, data, onlyMatchingComponentsNamed = null) { - this.performEmit(name, data, false, onlyMatchingComponentsNamed); - } - emitUp(name, data, onlyMatchingComponentsNamed = null) { - this.performEmit(name, data, true, onlyMatchingComponentsNamed); - } - emitSelf(name, data) { - this.doEmit(name, data); - } - performEmit(name, data, emitUp, matchingName) { - const components = findComponents(this, emitUp, matchingName); - components.forEach((component) => { - component.doEmit(name, data); - }); - } - doEmit(name, data) { - if (!this.listeners.has(name)) { - return; - } - const actions = this.listeners.get(name) || []; - actions.forEach((action) => { - this.action(action, data, 1); - }); - } - isTurboEnabled() { - return typeof Turbo !== 'undefined' && !this.element.closest('[data-turbo="false"]'); - } - tryStartingRequest() { - if (!this.backendRequest) { - this.performRequest(); - return; - } - this.isRequestPending = true; - } - performRequest() { - const thisPromiseResolve = this.nextRequestPromiseResolve; - this.resetPromise(); - this.unsyncedInputsTracker.resetUnsyncedFields(); - const filesToSend = {}; - for (const [key, value] of Object.entries(this.pendingFiles)) { - if (value.files) { - filesToSend[key] = value.files; - } - } - const requestConfig = { - props: this.valueStore.getOriginalProps(), - actions: this.pendingActions, - updated: this.valueStore.getDirtyProps(), - children: {}, - updatedPropsFromParent: this.valueStore.getUpdatedPropsFromParent(), - files: filesToSend, - }; - this.hooks.triggerHook('request:started', requestConfig); - this.backendRequest = this.backend.makeRequest(requestConfig.props, requestConfig.actions, requestConfig.updated, requestConfig.children, requestConfig.updatedPropsFromParent, requestConfig.files); - this.hooks.triggerHook('loading.state:started', this.element, this.backendRequest); - this.pendingActions = []; - this.valueStore.flushDirtyPropsToPending(); - this.isRequestPending = false; - this.backendRequest.promise.then(async (response) => { - const backendResponse = new BackendResponse(response); - const html = await backendResponse.getBody(); - for (const input of Object.values(this.pendingFiles)) { - input.value = ''; - } - const headers = backendResponse.response.headers; - if (!headers.get('Content-Type')?.includes('application/vnd.live-component+html') && - !headers.get('X-Live-Redirect')) { - const controls = { displayError: true }; - this.valueStore.pushPendingPropsBackToDirty(); - this.hooks.triggerHook('response:error', backendResponse, controls); - if (controls.displayError) { - this.renderError(html); - } - this.backendRequest = null; - thisPromiseResolve(backendResponse); - return response; - } - this.processRerender(html, backendResponse); - const liveUrl = backendResponse.getLiveUrl(); - if (liveUrl) { - history.replaceState(history.state, '', new URL(liveUrl + window.location.hash, window.location.origin)); - } - this.backendRequest = null; - thisPromiseResolve(backendResponse); - if (this.isRequestPending) { - this.isRequestPending = false; - this.performRequest(); - } - return response; - }); - } - processRerender(html, backendResponse) { - const controls = { shouldRender: true }; - this.hooks.triggerHook('render:started', html, backendResponse, controls); - if (!controls.shouldRender) { - return; - } - if (backendResponse.response.headers.get('Location')) { - if (this.isTurboEnabled()) { - Turbo.visit(backendResponse.response.headers.get('Location')); - } - else { - window.location.href = backendResponse.response.headers.get('Location') || ''; - } - return; - } - this.hooks.triggerHook('loading.state:finished', this.element); - const modifiedModelValues = {}; - Object.keys(this.valueStore.getDirtyProps()).forEach((modelName) => { - modifiedModelValues[modelName] = this.valueStore.get(modelName); - }); - let newElement; - try { - newElement = htmlToElement(html); - if (!newElement.matches('[data-controller~=live]')) { - throw new Error('A live component template must contain a single root controller element.'); - } - } - catch (error) { - console.error(`There was a problem with the '${this.name}' component HTML returned:`, { - id: this.id, - }); - throw error; - } - this.externalMutationTracker.handlePendingChanges(); - this.externalMutationTracker.stop(); - executeMorphdom(this.element, newElement, this.unsyncedInputsTracker.getUnsyncedInputs(), (element) => getValueFromElement(element, this.valueStore), this.externalMutationTracker); - this.externalMutationTracker.start(); - const newProps = this.elementDriver.getComponentProps(); - this.valueStore.reinitializeAllProps(newProps); - const eventsToEmit = this.elementDriver.getEventsToEmit(); - const browserEventsToDispatch = this.elementDriver.getBrowserEventsToDispatch(); - Object.keys(modifiedModelValues).forEach((modelName) => { - this.valueStore.set(modelName, modifiedModelValues[modelName]); - }); - eventsToEmit.forEach(({ event, data, target, componentName }) => { - if (target === 'up') { - this.emitUp(event, data, componentName); - return; - } - if (target === 'self') { - this.emitSelf(event, data); - return; - } - this.emit(event, data, componentName); - }); - browserEventsToDispatch.forEach(({ event, payload }) => { - this.element.dispatchEvent(new CustomEvent(event, { - detail: payload, - bubbles: true, - })); - }); - this.hooks.triggerHook('render:finished', this); - } - calculateDebounce(debounce) { - if (debounce === true) { - return this.defaultDebounce; - } - if (debounce === false) { - return 0; - } - return debounce; - } - clearRequestDebounceTimeout() { - if (this.requestDebounceTimeout) { - clearTimeout(this.requestDebounceTimeout); - this.requestDebounceTimeout = null; - } - } - debouncedStartRequest(debounce) { - this.clearRequestDebounceTimeout(); - this.requestDebounceTimeout = window.setTimeout(() => { - this.render(); - }, this.calculateDebounce(debounce)); - } - renderError(html) { - let modal = document.getElementById('live-component-error'); - if (modal) { - modal.innerHTML = ''; - } - else { - modal = document.createElement('div'); - modal.id = 'live-component-error'; - modal.style.padding = '50px'; - modal.style.backgroundColor = 'rgba(0, 0, 0, .5)'; - modal.style.zIndex = '100000'; - modal.style.position = 'fixed'; - modal.style.top = '0px'; - modal.style.bottom = '0px'; - modal.style.left = '0px'; - modal.style.right = '0px'; - modal.style.display = 'flex'; - modal.style.flexDirection = 'column'; - } - const iframe = document.createElement('iframe'); - iframe.style.borderRadius = '5px'; - iframe.style.flexGrow = '1'; - modal.appendChild(iframe); - document.body.prepend(modal); - document.body.style.overflow = 'hidden'; - if (iframe.contentWindow) { - iframe.contentWindow.document.open(); - iframe.contentWindow.document.write(html); - iframe.contentWindow.document.close(); - } - const closeModal = (modal) => { - if (modal) { - modal.outerHTML = ''; - } - document.body.style.overflow = 'visible'; - }; - modal.addEventListener('click', () => closeModal(modal)); - modal.setAttribute('tabindex', '0'); - modal.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - closeModal(modal); - } - }); - modal.focus(); - } - resetPromise() { - this.nextRequestPromise = new Promise((resolve) => { - this.nextRequestPromiseResolve = resolve; - }); - } - _updateFromParentProps(props) { - const isChanged = this.valueStore.storeNewPropsFromParent(props); - if (isChanged) { - this.render(); - } - } -} -function proxifyComponent(component) { - return new Proxy(component, { - get(component, prop) { - if (prop in component || typeof prop !== 'string') { - if (typeof component[prop] === 'function') { - const callable = component[prop]; - return (...args) => { - return callable.apply(component, args); - }; - } - return Reflect.get(component, prop); - } - if (component.valueStore.has(prop)) { - return component.getData(prop); - } - return (args) => { - return component.action.apply(component, [prop, args]); - }; - }, - set(target, property, value) { - if (property in target) { - target[property] = value; - return true; - } - target.set(property, value); - return true; - }, +// src/live_controller.ts +var _LiveControllerDefault = class _LiveControllerDefault extends Controller { + constructor() { + super(...arguments); + this.pendingActionTriggerModelElement = null; + this.elementEventListeners = [ + { event: "input", callback: (event) => this.handleInputEvent(event) }, + { event: "change", callback: (event) => this.handleChangeEvent(event) } + ]; + this.pendingFiles = {}; + } + initialize() { + this.mutationObserver = new MutationObserver(this.onMutations.bind(this)); + this.createComponent(); + } + connect() { + this.connectComponent(); + this.mutationObserver.observe(this.element, { + attributes: true }); -} - -class StimulusElementDriver { - constructor(controller) { - this.controller = controller; - } - getModelName(element) { - const modelDirective = getModelDirectiveFromElement(element, false); - if (!modelDirective) { - return null; - } - return modelDirective.action; - } - getComponentProps() { - return this.controller.propsValue; - } - getEventsToEmit() { - return this.controller.eventsToEmitValue; - } - getBrowserEventsToDispatch() { - return this.controller.eventsToDispatchValue; - } -} - -function getModelBinding (modelDirective) { - let shouldRender = true; - let targetEventName = null; + } + disconnect() { + this.disconnectComponent(); + this.mutationObserver.disconnect(); + } + /** + * Called to update one piece of the model. + * + * + */ + update(event) { + if (event.type === "input" || event.type === "change") { + throw new Error( + `Since LiveComponents 2.3, you no longer need data-action="live#update" on form elements. Found on element: ${getElementAsTagText( + event.currentTarget + )}` + ); + } + this.updateModelFromElementEvent(event.currentTarget, null); + } + action(event) { + const params = event.params; + if (!params.action) { + throw new Error( + `No action name provided on element: ${getElementAsTagText( + event.currentTarget + )}. Did you forget to add the "data-live-action-param" attribute?` + ); + } + const rawAction = params.action; + const actionArgs = { ...params }; + delete actionArgs.action; + const directives = parseDirectives(rawAction); let debounce = false; - let minLength = null; - let maxLength = null; - let minValue = null; - let maxValue = null; - modelDirective.modifiers.forEach((modifier) => { + directives.forEach((directive) => { + let pendingFiles = {}; + const validModifiers = /* @__PURE__ */ new Map(); + validModifiers.set("stop", () => { + event.stopPropagation(); + }); + validModifiers.set("self", () => { + if (event.target !== event.currentTarget) { + return; + } + }); + validModifiers.set("debounce", (modifier) => { + debounce = modifier.value ? Number.parseInt(modifier.value) : true; + }); + validModifiers.set("files", (modifier) => { + if (!modifier.value) { + pendingFiles = this.pendingFiles; + } else if (this.pendingFiles[modifier.value]) { + pendingFiles[modifier.value] = this.pendingFiles[modifier.value]; + } + }); + directive.modifiers.forEach((modifier) => { + if (validModifiers.has(modifier.name)) { + const callable = validModifiers.get(modifier.name) ?? (() => { + }); + callable(modifier); + return; + } + console.warn( + `Unknown modifier ${modifier.name} in action "${rawAction}". Available modifiers are: ${Array.from( + validModifiers.keys() + ).join(", ")}.` + ); + }); + for (const [key, input] of Object.entries(pendingFiles)) { + if (input.files) { + this.component.files(key, input); + } + delete this.pendingFiles[key]; + } + this.component.action(directive.action, actionArgs, debounce); + if (getModelDirectiveFromElement(event.currentTarget, false)) { + this.pendingActionTriggerModelElement = event.currentTarget; + } + }); + } + $render() { + return this.component.render(); + } + emit(event) { + this.getEmitDirectives(event).forEach(({ name, data, nameMatch }) => { + this.component.emit(name, data, nameMatch); + }); + } + emitUp(event) { + this.getEmitDirectives(event).forEach(({ name, data, nameMatch }) => { + this.component.emitUp(name, data, nameMatch); + }); + } + emitSelf(event) { + this.getEmitDirectives(event).forEach(({ name, data }) => { + this.component.emitSelf(name, data); + }); + } + /** + * Update a model value. + * + * @param {string} model The model to update + * @param {any} value The new value + * @param {boolean} shouldRender Whether a re-render should be triggered + * @param {number|boolean} debounce + */ + $updateModel(model, value, shouldRender = true, debounce = true) { + return this.component.set(model, value, shouldRender, debounce); + } + propsUpdatedFromParentValueChanged() { + this.component._updateFromParentProps(this.propsUpdatedFromParentValue); + } + fingerprintValueChanged() { + this.component.fingerprint = this.fingerprintValue; + } + getEmitDirectives(event) { + const params = event.params; + if (!params.event) { + throw new Error( + `No event name provided on element: ${getElementAsTagText( + event.currentTarget + )}. Did you forget to add the "data-live-event-param" attribute?` + ); + } + const eventInfo = params.event; + const eventArgs = { ...params }; + delete eventArgs.event; + const directives = parseDirectives(eventInfo); + const emits = []; + directives.forEach((directive) => { + let nameMatch = null; + directive.modifiers.forEach((modifier) => { switch (modifier.name) { - case 'on': - if (!modifier.value) { - throw new Error(`The "on" modifier in ${modelDirective.getString()} requires a value - e.g. on(change).`); - } - if (!['input', 'change'].includes(modifier.value)) { - throw new Error(`The "on" modifier in ${modelDirective.getString()} only accepts the arguments "input" or "change".`); - } - targetEventName = modifier.value; - break; - case 'norender': - shouldRender = false; - break; - case 'debounce': - debounce = modifier.value ? Number.parseInt(modifier.value) : true; - break; - case 'min_length': - minLength = modifier.value ? Number.parseInt(modifier.value) : null; - break; - case 'max_length': - maxLength = modifier.value ? Number.parseInt(modifier.value) : null; - break; - case 'min_value': - minValue = modifier.value ? Number.parseFloat(modifier.value) : null; - break; - case 'max_value': - maxValue = modifier.value ? Number.parseFloat(modifier.value) : null; - break; - default: - throw new Error(`Unknown modifier "${modifier.name}" in data-model="${modelDirective.getString()}".`); - } + case "name": + nameMatch = modifier.value; + break; + default: + throw new Error(`Unknown modifier ${modifier.name} in event "${eventInfo}".`); + } + }); + emits.push({ + name: directive.action, + data: eventArgs, + nameMatch + }); }); - const [modelName, innerModelName] = modelDirective.action.split(':'); - return { - modelName, - innerModelName: innerModelName || null, - shouldRender, - debounce, - targetEventName, - minLength, - maxLength, - minValue, - maxValue, - }; -} - -class ChildComponentPlugin { - constructor(component) { - this.parentModelBindings = []; - this.component = component; - const modelDirectives = getAllModelDirectiveFromElements(this.component.element); - this.parentModelBindings = modelDirectives.map(getModelBinding); - } - attachToComponent(component) { - component.on('request:started', (requestData) => { - requestData.children = this.getChildrenFingerprints(); - }); - component.on('model:set', (model, value) => { - this.notifyParentModelChange(model, value); - }); - } - getChildrenFingerprints() { - const fingerprints = {}; - this.getChildren().forEach((child) => { - if (!child.id) { - throw new Error('missing id'); - } - fingerprints[child.id] = { - fingerprint: child.fingerprint, - tag: child.element.tagName.toLowerCase(), - }; - }); - return fingerprints; - } - notifyParentModelChange(modelName, value) { - const parentComponent = findParent(this.component); - if (!parentComponent) { - return; - } - this.parentModelBindings.forEach((modelBinding) => { - const childModelName = modelBinding.innerModelName || 'value'; - if (childModelName !== modelName) { - return; - } - parentComponent.set(modelBinding.modelName, value, modelBinding.shouldRender, modelBinding.debounce); - }); - } - getChildren() { - return findChildren(this.component); - } -} - -class LazyPlugin { - constructor() { - this.intersectionObserver = null; - } - attachToComponent(component) { - if ('lazy' !== component.element.attributes.getNamedItem('loading')?.value) { - return; - } - component.on('connect', () => { - this.getObserver().observe(component.element); - }); - component.on('disconnect', () => { - this.intersectionObserver?.unobserve(component.element); - }); - } - getObserver() { - if (!this.intersectionObserver) { - this.intersectionObserver = new IntersectionObserver((entries, observer) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - entry.target.dispatchEvent(new CustomEvent('live:appear')); - observer.unobserve(entry.target); - } - }); - }); - } - return this.intersectionObserver; - } -} - -class LoadingPlugin { - attachToComponent(component) { - component.on('loading.state:started', (element, request) => { - this.startLoading(component, element, request); - }); - component.on('loading.state:finished', (element) => { - this.finishLoading(component, element); - }); - this.finishLoading(component, component.element); - } - startLoading(component, targetElement, backendRequest) { - this.handleLoadingToggle(component, true, targetElement, backendRequest); - } - finishLoading(component, targetElement) { - this.handleLoadingToggle(component, false, targetElement, null); - } - handleLoadingToggle(component, isLoading, targetElement, backendRequest) { - if (isLoading) { - this.addAttributes(targetElement, ['busy']); - } - else { - this.removeAttributes(targetElement, ['busy']); - } - this.getLoadingDirectives(component, targetElement).forEach(({ element, directives }) => { - if (isLoading) { - this.addAttributes(element, ['data-live-is-loading']); - } - else { - this.removeAttributes(element, ['data-live-is-loading']); - } - directives.forEach((directive) => { - this.handleLoadingDirective(element, isLoading, directive, backendRequest); - }); - }); - } - handleLoadingDirective(element, isLoading, directive, backendRequest) { - const finalAction = parseLoadingAction(directive.action, isLoading); - const targetedActions = []; - const targetedModels = []; - let delay = 0; - const validModifiers = new Map(); - validModifiers.set('delay', (modifier) => { - if (!isLoading) { - return; - } - delay = modifier.value ? Number.parseInt(modifier.value) : 200; - }); - validModifiers.set('action', (modifier) => { - if (!modifier.value) { - throw new Error(`The "action" in data-loading must have an action name - e.g. action(foo). It's missing for "${directive.getString()}"`); - } - targetedActions.push(modifier.value); - }); - validModifiers.set('model', (modifier) => { - if (!modifier.value) { - throw new Error(`The "model" in data-loading must have an action name - e.g. model(foo). It's missing for "${directive.getString()}"`); - } - targetedModels.push(modifier.value); - }); - directive.modifiers.forEach((modifier) => { - if (validModifiers.has(modifier.name)) { - const callable = validModifiers.get(modifier.name) ?? (() => { }); - callable(modifier); - return; - } - throw new Error(`Unknown modifier "${modifier.name}" used in data-loading="${directive.getString()}". Available modifiers are: ${Array.from(validModifiers.keys()).join(', ')}.`); - }); - if (isLoading && - targetedActions.length > 0 && - backendRequest && - !backendRequest.containsOneOfActions(targetedActions)) { - return; - } - if (isLoading && - targetedModels.length > 0 && - backendRequest && - !backendRequest.areAnyModelsUpdated(targetedModels)) { - return; - } - let loadingDirective; - switch (finalAction) { - case 'show': - loadingDirective = () => this.showElement(element); - break; - case 'hide': - loadingDirective = () => this.hideElement(element); - break; - case 'addClass': - loadingDirective = () => this.addClass(element, directive.args); - break; - case 'removeClass': - loadingDirective = () => this.removeClass(element, directive.args); - break; - case 'addAttribute': - loadingDirective = () => this.addAttributes(element, directive.args); - break; - case 'removeAttribute': - loadingDirective = () => this.removeAttributes(element, directive.args); - break; - default: - throw new Error(`Unknown data-loading action "${finalAction}"`); - } - if (delay) { - window.setTimeout(() => { - if (backendRequest && !backendRequest.isResolved) { - loadingDirective(); - } - }, delay); - return; - } - loadingDirective(); - } - getLoadingDirectives(component, element) { - const loadingDirectives = []; - let matchingElements = [...Array.from(element.querySelectorAll('[data-loading]'))]; - matchingElements = matchingElements.filter((elt) => elementBelongsToThisComponent(elt, component)); - if (element.hasAttribute('data-loading')) { - matchingElements = [element, ...matchingElements]; - } - matchingElements.forEach((element) => { - if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { - throw new Error('Invalid Element Type'); - } - const directives = parseDirectives(element.dataset.loading || 'show'); - loadingDirectives.push({ - element, - directives, - }); - }); - return loadingDirectives; - } - showElement(element) { - element.style.display = 'revert'; - } - hideElement(element) { - element.style.display = 'none'; - } - addClass(element, classes) { - element.classList.add(...combineSpacedArray(classes)); - } - removeClass(element, classes) { - element.classList.remove(...combineSpacedArray(classes)); - if (element.classList.length === 0) { - element.removeAttribute('class'); - } - } - addAttributes(element, attributes) { - attributes.forEach((attribute) => { - element.setAttribute(attribute, ''); - }); - } - removeAttributes(element, attributes) { - attributes.forEach((attribute) => { - element.removeAttribute(attribute); - }); - } -} -const parseLoadingAction = (action, isLoading) => { - switch (action) { - case 'show': - return isLoading ? 'show' : 'hide'; - case 'hide': - return isLoading ? 'hide' : 'show'; - case 'addClass': - return isLoading ? 'addClass' : 'removeClass'; - case 'removeClass': - return isLoading ? 'removeClass' : 'addClass'; - case 'addAttribute': - return isLoading ? 'addAttribute' : 'removeAttribute'; - case 'removeAttribute': - return isLoading ? 'removeAttribute' : 'addAttribute'; - } - throw new Error(`Unknown data-loading action "${action}"`); -}; - -class PageUnloadingPlugin { - constructor() { - this.isConnected = false; - } - attachToComponent(component) { - component.on('render:started', (html, response, controls) => { - if (!this.isConnected) { - controls.shouldRender = false; - } - }); - component.on('connect', () => { - this.isConnected = true; - }); - component.on('disconnect', () => { - this.isConnected = false; - }); - } -} - -class PollingDirector { - constructor(component) { - this.isPollingActive = true; - this.pollingIntervals = []; - this.component = component; - } - addPoll(actionName, duration) { - this.polls.push({ actionName, duration }); - if (this.isPollingActive) { - this.initiatePoll(actionName, duration); - } - } - startAllPolling() { - if (this.isPollingActive) { - return; - } - this.isPollingActive = true; - this.polls.forEach(({ actionName, duration }) => { - this.initiatePoll(actionName, duration); - }); - } - stopAllPolling() { - this.isPollingActive = false; - this.pollingIntervals.forEach((interval) => { - clearInterval(interval); - }); - } - clearPolling() { - this.stopAllPolling(); - this.polls = []; - this.startAllPolling(); - } - initiatePoll(actionName, duration) { - let callback; - if (actionName === '$render') { - callback = () => { - this.component.render(); - }; - } - else { - callback = () => { - this.component.action(actionName, {}, 0); - }; - } - const timer = window.setInterval(() => { - callback(); - }, duration); - this.pollingIntervals.push(timer); - } -} - -class PollingPlugin { - attachToComponent(component) { - this.element = component.element; - this.pollingDirector = new PollingDirector(component); - this.initializePolling(); - component.on('connect', () => { - this.pollingDirector.startAllPolling(); - }); - component.on('disconnect', () => { - this.pollingDirector.stopAllPolling(); - }); - component.on('render:finished', () => { - this.initializePolling(); - }); - } - addPoll(actionName, duration) { - this.pollingDirector.addPoll(actionName, duration); - } - clearPolling() { - this.pollingDirector.clearPolling(); - } - initializePolling() { - this.clearPolling(); - if (this.element.dataset.poll === undefined) { - return; - } - const rawPollConfig = this.element.dataset.poll; - const directives = parseDirectives(rawPollConfig || '$render'); - directives.forEach((directive) => { - let duration = 2000; - directive.modifiers.forEach((modifier) => { - switch (modifier.name) { - case 'delay': - if (modifier.value) { - duration = Number.parseInt(modifier.value); - } - break; - default: - console.warn(`Unknown modifier "${modifier.name}" in data-poll "${rawPollConfig}".`); - } - }); - this.addPoll(directive.action, duration); - }); - } -} - -class SetValueOntoModelFieldsPlugin { - attachToComponent(component) { - this.synchronizeValueOfModelFields(component); - component.on('render:finished', () => { - this.synchronizeValueOfModelFields(component); - }); - } - synchronizeValueOfModelFields(component) { - component.element.querySelectorAll('[data-model]').forEach((element) => { - if (!(element instanceof HTMLElement)) { - throw new Error('Invalid element using data-model.'); - } - if (element instanceof HTMLFormElement) { - return; - } - if (!elementBelongsToThisComponent(element, component)) { - return; - } - const modelDirective = getModelDirectiveFromElement(element); - if (!modelDirective) { - return; - } - const modelName = modelDirective.action; - if (component.getUnsyncedModels().includes(modelName)) { - return; - } - if (component.valueStore.has(modelName)) { - setValueOnElement(element, component.valueStore.get(modelName)); - } - if (element instanceof HTMLSelectElement && !element.multiple) { - component.valueStore.set(modelName, getValueFromElement(element, component.valueStore)); - } - }); - } -} - -class ValidatedFieldsPlugin { - attachToComponent(component) { - component.on('model:set', (modelName) => { - this.handleModelSet(modelName, component.valueStore); - }); - } - handleModelSet(modelName, valueStore) { - if (valueStore.has('validatedFields')) { - const validatedFields = [...valueStore.get('validatedFields')]; - if (!validatedFields.includes(modelName)) { - validatedFields.push(modelName); - } - valueStore.set('validatedFields', validatedFields); - } + return emits; + } + createComponent() { + const id = this.element.id || null; + this.component = new Component( + this.element, + this.nameValue, + this.propsValue, + this.listenersValue, + id, + _LiveControllerDefault.backendFactory(this), + new StimulusElementDriver(this) + ); + this.proxiedComponent = proxifyComponent(this.component); + Object.defineProperty(this.element, "__component", { + value: this.proxiedComponent, + writable: true + }); + if (this.hasDebounceValue) { + this.component.defaultDebounce = this.debounceValue; + } + const plugins = [ + new LoadingPlugin_default(), + new LazyPlugin_default(), + new ValidatedFieldsPlugin_default(), + new PageUnloadingPlugin_default(), + new PollingPlugin_default(), + new SetValueOntoModelFieldsPlugin_default(), + new ChildComponentPlugin_default(this.component) + ]; + plugins.forEach((plugin) => { + this.component.addPlugin(plugin); + }); + } + connectComponent() { + this.component.connect(); + this.mutationObserver.observe(this.element, { + attributes: true + }); + this.elementEventListeners.forEach(({ event, callback }) => { + this.component.element.addEventListener(event, callback); + }); + this.dispatchEvent("connect"); + } + disconnectComponent() { + this.component.disconnect(); + this.elementEventListeners.forEach(({ event, callback }) => { + this.component.element.removeEventListener(event, callback); + }); + this.dispatchEvent("disconnect"); + } + handleInputEvent(event) { + const target = event.target; + if (!target) { + return; + } + this.updateModelFromElementEvent(target, "input"); + } + handleChangeEvent(event) { + const target = event.target; + if (!target) { + return; + } + this.updateModelFromElementEvent(target, "change"); + } + /** + * Sets a model given an element and some event. + * + * This parses the "data-model" from the element and takes + * into account modifiers like "debounce", "norender" and "on()". + * + * This is used, for example, the grab the new value from an input + * on "change" and set that new value onto the model. + * + * It's also used to, on click, set the value from a button + * with data-model="" and data-value"". + * + * @param element + * @param eventName If specified (e.g. "input" or "change"), the model may + * skip updating if the on() modifier is passed (e.g. on(change)). + * If not passed, the model will always be updated. + */ + updateModelFromElementEvent(element, eventName) { + if (!elementBelongsToThisComponent(element, this.component)) { + return; + } + if (!(element instanceof HTMLElement)) { + throw new Error("Could not update model for non HTMLElement"); + } + if (element instanceof HTMLInputElement && element.type === "file") { + const key = element.name; + if (element.files?.length) { + this.pendingFiles[key] = element; + } else if (this.pendingFiles[key]) { + delete this.pendingFiles[key]; + } + } + const modelDirective = getModelDirectiveFromElement(element, false); + if (!modelDirective) { + return; + } + const modelBinding = get_model_binding_default(modelDirective); + if (!modelBinding.targetEventName) { + modelBinding.targetEventName = "input"; + } + if (this.pendingActionTriggerModelElement === element) { + modelBinding.shouldRender = false; + } + if (eventName === "change" && modelBinding.targetEventName === "input") { + modelBinding.targetEventName = "change"; + } + if (eventName && modelBinding.targetEventName !== eventName) { + return; + } + if (false === modelBinding.debounce) { + if (modelBinding.targetEventName === "input") { + modelBinding.debounce = true; + } else { + modelBinding.debounce = 0; + } + } + const finalValue = getValueFromElement(element, this.component.valueStore); + if (isTextualInputElement(element) || isTextareaElement(element)) { + if (modelBinding.minLength !== null && typeof finalValue === "string" && finalValue.length < modelBinding.minLength) { + return; + } + if (modelBinding.maxLength !== null && typeof finalValue === "string" && finalValue.length > modelBinding.maxLength) { + return; + } } -} - -class LiveControllerDefault extends Controller { - constructor() { - super(...arguments); - this.pendingActionTriggerModelElement = null; - this.elementEventListeners = [ - { event: 'input', callback: (event) => this.handleInputEvent(event) }, - { event: 'change', callback: (event) => this.handleChangeEvent(event) }, - ]; - this.pendingFiles = {}; - } - initialize() { - this.mutationObserver = new MutationObserver(this.onMutations.bind(this)); + if (isNumericalInputElement(element)) { + const numericValue = Number(finalValue); + if (modelBinding.minValue !== null && numericValue < modelBinding.minValue) { + return; + } + if (modelBinding.maxValue !== null && numericValue > modelBinding.maxValue) { + return; + } + } + this.component.set(modelBinding.modelName, finalValue, modelBinding.shouldRender, modelBinding.debounce); + } + dispatchEvent(name, detail = {}, canBubble = true, cancelable = false) { + detail.controller = this; + detail.component = this.proxiedComponent; + this.dispatch(name, { detail, prefix: "live", cancelable, bubbles: canBubble }); + } + onMutations(mutations) { + mutations.forEach((mutation) => { + if (mutation.type === "attributes" && mutation.attributeName === "id" && this.element.id !== this.component.id) { + this.disconnectComponent(); this.createComponent(); - } - connect() { this.connectComponent(); - this.mutationObserver.observe(this.element, { - attributes: true, - }); - } - disconnect() { - this.disconnectComponent(); - this.mutationObserver.disconnect(); - } - update(event) { - if (event.type === 'input' || event.type === 'change') { - throw new Error(`Since LiveComponents 2.3, you no longer need data-action="live#update" on form elements. Found on element: ${getElementAsTagText(event.currentTarget)}`); - } - this.updateModelFromElementEvent(event.currentTarget, null); - } - action(event) { - const params = event.params; - if (!params.action) { - throw new Error(`No action name provided on element: ${getElementAsTagText(event.currentTarget)}. Did you forget to add the "data-live-action-param" attribute?`); - } - const rawAction = params.action; - const actionArgs = { ...params }; - delete actionArgs.action; - const directives = parseDirectives(rawAction); - let debounce = false; - directives.forEach((directive) => { - let pendingFiles = {}; - const validModifiers = new Map(); - validModifiers.set('stop', () => { - event.stopPropagation(); - }); - validModifiers.set('self', () => { - if (event.target !== event.currentTarget) { - return; - } - }); - validModifiers.set('debounce', (modifier) => { - debounce = modifier.value ? Number.parseInt(modifier.value) : true; - }); - validModifiers.set('files', (modifier) => { - if (!modifier.value) { - pendingFiles = this.pendingFiles; - } - else if (this.pendingFiles[modifier.value]) { - pendingFiles[modifier.value] = this.pendingFiles[modifier.value]; - } - }); - directive.modifiers.forEach((modifier) => { - if (validModifiers.has(modifier.name)) { - const callable = validModifiers.get(modifier.name) ?? (() => { }); - callable(modifier); - return; - } - console.warn(`Unknown modifier ${modifier.name} in action "${rawAction}". Available modifiers are: ${Array.from(validModifiers.keys()).join(', ')}.`); - }); - for (const [key, input] of Object.entries(pendingFiles)) { - if (input.files) { - this.component.files(key, input); - } - delete this.pendingFiles[key]; - } - this.component.action(directive.action, actionArgs, debounce); - if (getModelDirectiveFromElement(event.currentTarget, false)) { - this.pendingActionTriggerModelElement = event.currentTarget; - } - }); - } - $render() { - return this.component.render(); - } - emit(event) { - this.getEmitDirectives(event).forEach(({ name, data, nameMatch }) => { - this.component.emit(name, data, nameMatch); - }); - } - emitUp(event) { - this.getEmitDirectives(event).forEach(({ name, data, nameMatch }) => { - this.component.emitUp(name, data, nameMatch); - }); - } - emitSelf(event) { - this.getEmitDirectives(event).forEach(({ name, data }) => { - this.component.emitSelf(name, data); - }); - } - $updateModel(model, value, shouldRender = true, debounce = true) { - return this.component.set(model, value, shouldRender, debounce); - } - propsUpdatedFromParentValueChanged() { - this.component._updateFromParentProps(this.propsUpdatedFromParentValue); - } - fingerprintValueChanged() { - this.component.fingerprint = this.fingerprintValue; - } - getEmitDirectives(event) { - const params = event.params; - if (!params.event) { - throw new Error(`No event name provided on element: ${getElementAsTagText(event.currentTarget)}. Did you forget to add the "data-live-event-param" attribute?`); - } - const eventInfo = params.event; - const eventArgs = { ...params }; - delete eventArgs.event; - const directives = parseDirectives(eventInfo); - const emits = []; - directives.forEach((directive) => { - let nameMatch = null; - directive.modifiers.forEach((modifier) => { - switch (modifier.name) { - case 'name': - nameMatch = modifier.value; - break; - default: - throw new Error(`Unknown modifier ${modifier.name} in event "${eventInfo}".`); - } - }); - emits.push({ - name: directive.action, - data: eventArgs, - nameMatch, - }); - }); - return emits; - } - createComponent() { - const id = this.element.id || null; - this.component = new Component(this.element, this.nameValue, this.propsValue, this.listenersValue, id, LiveControllerDefault.backendFactory(this), new StimulusElementDriver(this)); - this.proxiedComponent = proxifyComponent(this.component); - Object.defineProperty(this.element, '__component', { - value: this.proxiedComponent, - writable: true, - }); - if (this.hasDebounceValue) { - this.component.defaultDebounce = this.debounceValue; - } - const plugins = [ - new LoadingPlugin(), - new LazyPlugin(), - new ValidatedFieldsPlugin(), - new PageUnloadingPlugin(), - new PollingPlugin(), - new SetValueOntoModelFieldsPlugin(), - new ChildComponentPlugin(this.component), - ]; - plugins.forEach((plugin) => { - this.component.addPlugin(plugin); - }); - } - connectComponent() { - this.component.connect(); - this.mutationObserver.observe(this.element, { - attributes: true, - }); - this.elementEventListeners.forEach(({ event, callback }) => { - this.component.element.addEventListener(event, callback); - }); - this.dispatchEvent('connect'); - } - disconnectComponent() { - this.component.disconnect(); - this.elementEventListeners.forEach(({ event, callback }) => { - this.component.element.removeEventListener(event, callback); - }); - this.dispatchEvent('disconnect'); - } - handleInputEvent(event) { - const target = event.target; - if (!target) { - return; - } - this.updateModelFromElementEvent(target, 'input'); - } - handleChangeEvent(event) { - const target = event.target; - if (!target) { - return; - } - this.updateModelFromElementEvent(target, 'change'); - } - updateModelFromElementEvent(element, eventName) { - if (!elementBelongsToThisComponent(element, this.component)) { - return; - } - if (!(element instanceof HTMLElement)) { - throw new Error('Could not update model for non HTMLElement'); - } - if (element instanceof HTMLInputElement && element.type === 'file') { - const key = element.name; - if (element.files?.length) { - this.pendingFiles[key] = element; - } - else if (this.pendingFiles[key]) { - delete this.pendingFiles[key]; - } - } - const modelDirective = getModelDirectiveFromElement(element, false); - if (!modelDirective) { - return; - } - const modelBinding = getModelBinding(modelDirective); - if (!modelBinding.targetEventName) { - modelBinding.targetEventName = 'input'; - } - if (this.pendingActionTriggerModelElement === element) { - modelBinding.shouldRender = false; - } - if (eventName === 'change' && modelBinding.targetEventName === 'input') { - modelBinding.targetEventName = 'change'; - } - if (eventName && modelBinding.targetEventName !== eventName) { - return; - } - if (false === modelBinding.debounce) { - if (modelBinding.targetEventName === 'input') { - modelBinding.debounce = true; - } - else { - modelBinding.debounce = 0; - } - } - const finalValue = getValueFromElement(element, this.component.valueStore); - if (isTextualInputElement(element) || isTextareaElement(element)) { - if (modelBinding.minLength !== null && - typeof finalValue === 'string' && - finalValue.length < modelBinding.minLength) { - return; - } - if (modelBinding.maxLength !== null && - typeof finalValue === 'string' && - finalValue.length > modelBinding.maxLength) { - return; - } - } - if (isNumericalInputElement(element)) { - const numericValue = Number(finalValue); - if (modelBinding.minValue !== null && numericValue < modelBinding.minValue) { - return; - } - if (modelBinding.maxValue !== null && numericValue > modelBinding.maxValue) { - return; - } - } - this.component.set(modelBinding.modelName, finalValue, modelBinding.shouldRender, modelBinding.debounce); - } - dispatchEvent(name, detail = {}, canBubble = true, cancelable = false) { - detail.controller = this; - detail.component = this.proxiedComponent; - this.dispatch(name, { detail, prefix: 'live', cancelable, bubbles: canBubble }); - } - onMutations(mutations) { - mutations.forEach((mutation) => { - if (mutation.type === 'attributes' && - mutation.attributeName === 'id' && - this.element.id !== this.component.id) { - this.disconnectComponent(); - this.createComponent(); - this.connectComponent(); - } - }); - } -} -LiveControllerDefault.values = { - name: String, - url: String, - props: { type: Object, default: {} }, - propsUpdatedFromParent: { type: Object, default: {} }, - listeners: { type: Array, default: [] }, - eventsToEmit: { type: Array, default: [] }, - eventsToDispatch: { type: Array, default: [] }, - debounce: { type: Number, default: 150 }, - fingerprint: { type: String, default: '' }, - requestMethod: { type: String, default: 'post' }, + } + }); + } +}; +_LiveControllerDefault.values = { + name: String, + url: String, + props: { type: Object, default: {} }, + propsUpdatedFromParent: { type: Object, default: {} }, + listeners: { type: Array, default: [] }, + eventsToEmit: { type: Array, default: [] }, + eventsToDispatch: { type: Array, default: [] }, + debounce: { type: Number, default: 150 }, + fingerprint: { type: String, default: "" }, + requestMethod: { type: String, default: "post" } +}; +_LiveControllerDefault.backendFactory = (controller) => new Backend_default(controller.urlValue, controller.requestMethodValue); +var LiveControllerDefault = _LiveControllerDefault; +export { + Component, + LiveControllerDefault as default, + getComponent }; -LiveControllerDefault.backendFactory = (controller) => new Backend(controller.urlValue, controller.requestMethodValue); - -export { Component, LiveControllerDefault as default, getComponent }; diff --git a/src/LiveComponent/assets/dist/morphdom.d.ts b/src/LiveComponent/assets/dist/morphdom.d.ts deleted file mode 100644 index 81b2aca2b93..00000000000 --- a/src/LiveComponent/assets/dist/morphdom.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import type ExternalMutationTracker from './Rendering/ExternalMutationTracker'; -export declare function executeMorphdom(rootFromElement: HTMLElement, rootToElement: HTMLElement, modifiedFieldElements: Array, getElementValue: (element: HTMLElement) => any, externalMutationTracker: ExternalMutationTracker): void; diff --git a/src/LiveComponent/assets/dist/normalize_attributes_for_comparison.d.ts b/src/LiveComponent/assets/dist/normalize_attributes_for_comparison.d.ts deleted file mode 100644 index cd7bd6b2869..00000000000 --- a/src/LiveComponent/assets/dist/normalize_attributes_for_comparison.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function normalizeAttributesForComparison(element: HTMLElement): void; diff --git a/src/LiveComponent/assets/dist/string_utils.d.ts b/src/LiveComponent/assets/dist/string_utils.d.ts deleted file mode 100644 index 87d65fe50f5..00000000000 --- a/src/LiveComponent/assets/dist/string_utils.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export declare function combineSpacedArray(parts: Array): string[]; -export declare function trimAll(str: string): string; -export declare function normalizeModelName(model: string): string; diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index e4d3f3fd989..42e76498f65 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -1,19 +1,20 @@ import { Controller } from '@hotwired/stimulus'; -export type Point = { + +type Point = { lat: number; lng: number; }; -export type Identifier = string; -export type WithIdentifier> = T & { +type Identifier = string; +type WithIdentifier> = T & { '@id': Identifier; }; type ExtraData = Record; -export declare const IconTypes: { +declare const IconTypes: { readonly Url: "url"; readonly Svg: "svg"; readonly UxIcon: "ux-icon"; }; -export type Icon = { +type Icon = { width: number; height: number; } & ({ @@ -27,7 +28,7 @@ export type Icon = { type: typeof IconTypes.Svg; html: string; }); -export type MapDefinition = { +type MapDefinition = { center: Point | null; zoom: number | null; minZoom: number | null; @@ -36,7 +37,7 @@ export type MapDefinition = { bridgeOptions?: BridgeMapOptions; extra: ExtraData; }; -export type MarkerDefinition = WithIdentifier<{ +type MarkerDefinition = WithIdentifier<{ position: Point; title: string | null; infoWindow?: Omit, 'position'>; @@ -45,7 +46,7 @@ export type MarkerDefinition = Wit bridgeOptions?: BridgeMarkerOptions; extra: ExtraData; }>; -export type PolygonDefinition = WithIdentifier<{ +type PolygonDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; points: Array | Array>; title: string | null; @@ -53,7 +54,7 @@ export type PolygonDefinition = W bridgeOptions?: BridgePolygonOptions; extra: ExtraData; }>; -export type PolylineDefinition = WithIdentifier<{ +type PolylineDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; points: Array; title: string | null; @@ -61,7 +62,7 @@ export type PolylineDefinition = bridgeOptions?: BridgePolylineOptions; extra: ExtraData; }>; -export type CircleDefinition = WithIdentifier<{ +type CircleDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; center: Point; radius: number; @@ -70,7 +71,7 @@ export type CircleDefinition = Wit bridgeOptions?: BridgeCircleOptions; extra: ExtraData; }>; -export type RectangleDefinition = WithIdentifier<{ +type RectangleDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; southWest: Point; northEast: Point; @@ -79,7 +80,7 @@ export type RectangleDefinition bridgeOptions?: BridgeRectangleOptions; extra: ExtraData; }>; -export type InfoWindowDefinition = { +type InfoWindowDefinition = { headerContent: string | null; content: string | null; position: Point; @@ -89,7 +90,7 @@ export type InfoWindowDefinition = { bridgeOptions?: BridgeInfoWindowOptions; extra: ExtraData; }; -export default abstract class extends Controller { +declare abstract class export_default extends Controller { static values: { providerOptions: ObjectConstructor; center: ObjectConstructor; @@ -192,4 +193,5 @@ export default abstract class this.createMarker({ definition })); + this.polygonsValue.forEach((definition) => this.createPolygon({ definition })); + this.polylinesValue.forEach((definition) => this.createPolyline({ definition })); + this.circlesValue.forEach((definition) => this.createCircle({ definition })); + this.rectanglesValue.forEach((definition) => this.createRectangle({ definition })); + if (this.fitBoundsToMarkersValue) { + this.doFitBoundsToMarkers(); } - connect() { - const extra = this.hasExtraValue ? this.extraValue : {}; - const mapDefinition = { - center: this.hasCenterValue ? this.centerValue : null, - zoom: this.hasZoomValue ? this.zoomValue : null, - minZoom: this.hasMinZoomValue ? this.minZoomValue : null, - maxZoom: this.hasMaxZoomValue ? this.maxZoomValue : null, - options: this.optionsValue, - extra, - }; - this.dispatchEvent('pre-connect', mapDefinition); - this.createMarker = this.createDrawingFactory('marker', this.markers, this.doCreateMarker.bind(this)); - this.createPolygon = this.createDrawingFactory('polygon', this.polygons, this.doCreatePolygon.bind(this)); - this.createPolyline = this.createDrawingFactory('polyline', this.polylines, this.doCreatePolyline.bind(this)); - this.createCircle = this.createDrawingFactory('circle', this.circles, this.doCreateCircle.bind(this)); - this.createRectangle = this.createDrawingFactory('rectangle', this.rectangles, this.doCreateRectangle.bind(this)); - this.map = this.doCreateMap({ definition: mapDefinition }); - this.markersValue.forEach((definition) => this.createMarker({ definition })); - this.polygonsValue.forEach((definition) => this.createPolygon({ definition })); - this.polylinesValue.forEach((definition) => this.createPolyline({ definition })); - this.circlesValue.forEach((definition) => this.createCircle({ definition })); - this.rectanglesValue.forEach((definition) => this.createRectangle({ definition })); - if (this.fitBoundsToMarkersValue) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: [...this.markers.values()], - polygons: [...this.polygons.values()], - polylines: [...this.polylines.values()], - circles: [...this.circles.values()], - rectangles: [...this.rectangles.values()], - infoWindows: this.infoWindows, - extra, - }); - this.isConnected = true; + this.dispatchEvent("connect", { + map: this.map, + markers: [...this.markers.values()], + polygons: [...this.polygons.values()], + polylines: [...this.polylines.values()], + circles: [...this.circles.values()], + rectangles: [...this.rectangles.values()], + infoWindows: this.infoWindows, + extra + }); + this.isConnected = true; + } + //region Public API + createInfoWindow({ + definition, + element + }) { + this.dispatchEvent("info-window:before-create", { definition, element }); + const infoWindow = this.doCreateInfoWindow({ definition, element }); + this.dispatchEvent("info-window:after-create", { infoWindow, definition, element }); + this.infoWindows.push(infoWindow); + return infoWindow; + } + markersValueChanged() { + if (!this.isConnected) { + return; } - createInfoWindow({ definition, element, }) { - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, definition, element }); - this.infoWindows.push(infoWindow); - return infoWindow; + this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker); + if (this.fitBoundsToMarkersValue) { + this.doFitBoundsToMarkers(); } - markersValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker); - if (this.fitBoundsToMarkersValue) { - this.doFitBoundsToMarkers(); - } + } + polygonsValueChanged() { + if (!this.isConnected) { + return; } - polygonsValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon); + this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon); + } + polylinesValueChanged() { + if (!this.isConnected) { + return; } - polylinesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline); + this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline); + } + circlesValueChanged() { + if (!this.isConnected) { + return; } - circlesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle); + this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle); + } + rectanglesValueChanged() { + if (!this.isConnected) { + return; } - rectanglesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle); - } - createDrawingFactory(type, draws, factory) { - const eventBefore = `${type}:before-create`; - const eventAfter = `${type}:after-create`; - return ({ definition }) => { - this.dispatchEvent(eventBefore, { definition }); - if (typeof definition.rawOptions !== 'undefined') { - console.warn(`[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, definition); - } - const drawing = factory({ definition }); - this.dispatchEvent(eventAfter, { [type]: drawing, definition }); - draws.set(definition['@id'], drawing); - return drawing; - }; - } - onDrawChanged(draws, newDrawDefinitions, factory, remover) { - const idsToRemove = new Set(draws.keys()); - newDrawDefinitions.forEach((definition) => { - idsToRemove.delete(definition['@id']); - }); - idsToRemove.forEach((id) => { - const draw = draws.get(id); - remover(draw); - draws.delete(id); - }); - newDrawDefinitions.forEach((definition) => { - if (!draws.has(definition['@id'])) { - factory({ definition }); - } - }); - } -} -default_1.values = { - providerOptions: Object, - center: Object, - zoom: Number, - minZoom: Number, - maxZoom: Number, - fitBoundsToMarkers: Boolean, - markers: Array, - polygons: Array, - polylines: Array, - circles: Array, - rectangles: Array, - options: Object, - extra: Object, + this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle); + } + createDrawingFactory(type, draws, factory) { + const eventBefore = `${type}:before-create`; + const eventAfter = `${type}:after-create`; + return ({ definition }) => { + this.dispatchEvent(eventBefore, { definition }); + if (typeof definition.rawOptions !== "undefined") { + console.warn( + `[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, + definition + ); + } + const drawing = factory({ definition }); + this.dispatchEvent(eventAfter, { [type]: drawing, definition }); + draws.set(definition["@id"], drawing); + return drawing; + }; + } + onDrawChanged(draws, newDrawDefinitions, factory, remover) { + const idsToRemove = new Set(draws.keys()); + newDrawDefinitions.forEach((definition) => { + idsToRemove.delete(definition["@id"]); + }); + idsToRemove.forEach((id) => { + const draw = draws.get(id); + remover(draw); + draws.delete(id); + }); + newDrawDefinitions.forEach((definition) => { + if (!draws.has(definition["@id"])) { + factory({ definition }); + } + }); + } + //endregion +}; +abstract_map_controller_default.values = { + providerOptions: Object, + center: Object, + zoom: Number, + minZoom: Number, + maxZoom: Number, + fitBoundsToMarkers: Boolean, + markers: Array, + polygons: Array, + polylines: Array, + circles: Array, + rectangles: Array, + options: Object, + extra: Object +}; +export { + IconTypes, + abstract_map_controller_default as default }; - -export { IconTypes, default_1 as default }; diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts index b9996d6be72..b99e974823b 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts @@ -1,8 +1,8 @@ -import type { LoaderOptions } from '@googlemaps/js-api-loader'; -import type { CircleDefinition, Icon, InfoWindowDefinition, MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map'; -import AbstractMapController from '@symfony/ux-map'; +import { LoaderOptions } from '@googlemaps/js-api-loader'; +import AbstractMapController, { MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, CircleDefinition, RectangleDefinition, InfoWindowDefinition, Icon } from '@symfony/ux-map'; + type MapOptions = Pick; -export default class extends AbstractMapController { +declare class export_default extends AbstractMapController { providerOptionsValue: Pick; map: google.maps.Map; connect(): Promise; @@ -46,4 +46,5 @@ export default class extends AbstractMapController this.createMarker({ definition })); - this.polygonsValue.forEach((definition) => this.createPolygon({ definition })); - this.polylinesValue.forEach((definition) => this.createPolyline({ definition })); - this.circlesValue.forEach((definition) => this.createCircle({ definition })); - this.rectanglesValue.forEach((definition) => this.createRectangle({ definition })); - if (this.fitBoundsToMarkersValue) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: [...this.markers.values()], - polygons: [...this.polygons.values()], - polylines: [...this.polylines.values()], - circles: [...this.circles.values()], - rectangles: [...this.rectangles.values()], - infoWindows: this.infoWindows, - extra, - }); - this.isConnected = true; - } - createInfoWindow({ definition, element, }) { - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, definition, element }); - this.infoWindows.push(infoWindow); - return infoWindow; - } - markersValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker); - if (this.fitBoundsToMarkersValue) { - this.doFitBoundsToMarkers(); - } - } - polygonsValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon); - } - polylinesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline); - } - circlesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle); - } - rectanglesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle); - } - createDrawingFactory(type, draws, factory) { - const eventBefore = `${type}:before-create`; - const eventAfter = `${type}:after-create`; - return ({ definition }) => { - this.dispatchEvent(eventBefore, { definition }); - if (typeof definition.rawOptions !== 'undefined') { - console.warn(`[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, definition); - } - const drawing = factory({ definition }); - this.dispatchEvent(eventAfter, { [type]: drawing, definition }); - draws.set(definition['@id'], drawing); - return drawing; - }; - } - onDrawChanged(draws, newDrawDefinitions, factory, remover) { - const idsToRemove = new Set(draws.keys()); - newDrawDefinitions.forEach((definition) => { - idsToRemove.delete(definition['@id']); - }); - idsToRemove.forEach((id) => { - const draw = draws.get(id); - remover(draw); - draws.delete(id); - }); - newDrawDefinitions.forEach((definition) => { - if (!draws.has(definition['@id'])) { - factory({ definition }); - } - }); - } -} -default_1.values = { - providerOptions: Object, - center: Object, - zoom: Number, - minZoom: Number, - maxZoom: Number, - fitBoundsToMarkers: Boolean, - markers: Array, - polygons: Array, - polylines: Array, - circles: Array, - rectangles: Array, - options: Object, - extra: Object, +var abstract_map_controller_default = class extends Controller { + constructor() { + super(...arguments); + this.markers = /* @__PURE__ */ new Map(); + this.polygons = /* @__PURE__ */ new Map(); + this.polylines = /* @__PURE__ */ new Map(); + this.circles = /* @__PURE__ */ new Map(); + this.rectangles = /* @__PURE__ */ new Map(); + this.infoWindows = []; + this.isConnected = false; + } + connect() { + const extra = this.hasExtraValue ? this.extraValue : {}; + const mapDefinition = { + center: this.hasCenterValue ? this.centerValue : null, + zoom: this.hasZoomValue ? this.zoomValue : null, + minZoom: this.hasMinZoomValue ? this.minZoomValue : null, + maxZoom: this.hasMaxZoomValue ? this.maxZoomValue : null, + options: this.optionsValue, + extra + }; + this.dispatchEvent("pre-connect", mapDefinition); + this.createMarker = this.createDrawingFactory("marker", this.markers, this.doCreateMarker.bind(this)); + this.createPolygon = this.createDrawingFactory("polygon", this.polygons, this.doCreatePolygon.bind(this)); + this.createPolyline = this.createDrawingFactory("polyline", this.polylines, this.doCreatePolyline.bind(this)); + this.createCircle = this.createDrawingFactory("circle", this.circles, this.doCreateCircle.bind(this)); + this.createRectangle = this.createDrawingFactory("rectangle", this.rectangles, this.doCreateRectangle.bind(this)); + this.map = this.doCreateMap({ definition: mapDefinition }); + this.markersValue.forEach((definition) => this.createMarker({ definition })); + this.polygonsValue.forEach((definition) => this.createPolygon({ definition })); + this.polylinesValue.forEach((definition) => this.createPolyline({ definition })); + this.circlesValue.forEach((definition) => this.createCircle({ definition })); + this.rectanglesValue.forEach((definition) => this.createRectangle({ definition })); + if (this.fitBoundsToMarkersValue) { + this.doFitBoundsToMarkers(); + } + this.dispatchEvent("connect", { + map: this.map, + markers: [...this.markers.values()], + polygons: [...this.polygons.values()], + polylines: [...this.polylines.values()], + circles: [...this.circles.values()], + rectangles: [...this.rectangles.values()], + infoWindows: this.infoWindows, + extra + }); + this.isConnected = true; + } + //region Public API + createInfoWindow({ + definition, + element + }) { + this.dispatchEvent("info-window:before-create", { definition, element }); + const infoWindow = this.doCreateInfoWindow({ definition, element }); + this.dispatchEvent("info-window:after-create", { infoWindow, definition, element }); + this.infoWindows.push(infoWindow); + return infoWindow; + } + markersValueChanged() { + if (!this.isConnected) { + return; + } + this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker); + if (this.fitBoundsToMarkersValue) { + this.doFitBoundsToMarkers(); + } + } + polygonsValueChanged() { + if (!this.isConnected) { + return; + } + this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon); + } + polylinesValueChanged() { + if (!this.isConnected) { + return; + } + this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline); + } + circlesValueChanged() { + if (!this.isConnected) { + return; + } + this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle); + } + rectanglesValueChanged() { + if (!this.isConnected) { + return; + } + this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle); + } + createDrawingFactory(type, draws, factory) { + const eventBefore = `${type}:before-create`; + const eventAfter = `${type}:after-create`; + return ({ definition }) => { + this.dispatchEvent(eventBefore, { definition }); + if (typeof definition.rawOptions !== "undefined") { + console.warn( + `[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, + definition + ); + } + const drawing = factory({ definition }); + this.dispatchEvent(eventAfter, { [type]: drawing, definition }); + draws.set(definition["@id"], drawing); + return drawing; + }; + } + onDrawChanged(draws, newDrawDefinitions, factory, remover) { + const idsToRemove = new Set(draws.keys()); + newDrawDefinitions.forEach((definition) => { + idsToRemove.delete(definition["@id"]); + }); + idsToRemove.forEach((id) => { + const draw = draws.get(id); + remover(draw); + draws.delete(id); + }); + newDrawDefinitions.forEach((definition) => { + if (!draws.has(definition["@id"])) { + factory({ definition }); + } + }); + } + //endregion +}; +abstract_map_controller_default.values = { + providerOptions: Object, + center: Object, + zoom: Number, + minZoom: Number, + maxZoom: Number, + fitBoundsToMarkers: Boolean, + markers: Array, + polygons: Array, + polylines: Array, + circles: Array, + rectangles: Array, + options: Object, + extra: Object }; -let _google; -let _loading = false; -let _loaded = false; -let _onLoadedCallbacks = []; -const parser = new DOMParser(); -class map_controller extends default_1 { - async connect() { - const onLoaded = () => super.connect(); - if (_loaded) { - onLoaded(); - return; - } - if (_loading) { - _onLoadedCallbacks.push(onLoaded); - return; - } - _loading = true; - _google = { maps: {} }; - let { libraries = [], ...loaderOptions } = this.providerOptionsValue; - const loader = new Loader(loaderOptions); - libraries = ['core', ...libraries.filter((library) => library !== 'core')]; - const librariesImplementations = await Promise.all(libraries.map((library) => loader.importLibrary(library))); - librariesImplementations.map((libraryImplementation, index) => { - if (typeof libraryImplementation !== 'object' || libraryImplementation === null) { - return; - } - const library = libraries[index]; - if (['marker', 'places', 'geometry', 'journeySharing', 'drawing', 'visualization'].includes(library)) { - _google.maps[library] = libraryImplementation; - } - else { - _google.maps = { ..._google.maps, ...libraryImplementation }; - } - }); - _loading = false; - _loaded = true; - onLoaded(); - _onLoadedCallbacks.forEach((callback) => callback()); - _onLoadedCallbacks = []; - } - centerValueChanged() { - if (this.map && this.hasCenterValue && this.centerValue) { - this.map.setCenter(this.centerValue); - } - } - zoomValueChanged() { - if (this.map && this.hasZoomValue && this.zoomValue) { - this.map.setZoom(this.zoomValue); - } - } - minZoomValueChanged() { - if (this.map && this.hasMinZoomValue && this.minZoomValue) { - this.map.setOptions({ minZoom: this.minZoomValue }); - } - } - maxZoomValueChanged() { - if (this.map && this.hasMaxZoomValue && this.maxZoomValue) { - this.map.setOptions({ maxZoom: this.maxZoomValue }); - } - } - dispatchEvent(name, payload = {}) { - payload.google = _google; - this.dispatch(name, { - prefix: 'ux:map', - detail: payload, - }); - } - doCreateMap({ definition }) { - const { center, zoom, minZoom, maxZoom, options, bridgeOptions = {} } = definition; - options.zoomControl = typeof options.zoomControlOptions !== 'undefined'; - options.mapTypeControl = typeof options.mapTypeControlOptions !== 'undefined'; - options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined'; - options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined'; - return new _google.maps.Map(this.element, { - center, - zoom, - minZoom, - maxZoom, - ...options, - ...bridgeOptions, - }); - } - doCreateMarker({ definition, }) { - const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; - const marker = new _google.maps.marker.AdvancedMarkerElement({ - position, - title, - map: this.map, - ...rawOptions, - ...bridgeOptions, - }); - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: marker }); - } - if (icon) { - this.doCreateIcon({ definition: icon, element: marker }); - } - return marker; - } - doRemoveMarker(marker) { - marker.map = null; - } - doCreatePolygon({ definition, }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const polygon = new _google.maps.Polygon({ - paths: points, - map: this.map, - ...rawOptions, - ...bridgeOptions, - }); - if (title) { - polygon.set('title', title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: polygon }); - } - return polygon; - } - doRemovePolygon(polygon) { - polygon.setMap(null); - } - doCreatePolyline({ definition, }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const polyline = new _google.maps.Polyline({ - path: points, - map: this.map, - ...rawOptions, - ...bridgeOptions, - }); - if (title) { - polyline.set('title', title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: polyline }); - } - return polyline; - } - doRemovePolyline(polyline) { - polyline.setMap(null); - } - doCreateCircle({ definition }) { - const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const circle = new _google.maps.Circle({ - center, - radius, - map: this.map, - ...rawOptions, - ...bridgeOptions, - }); - if (title) { - circle.set('title', title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: circle }); - } - return circle; - } - doRemoveCircle(circle) { - circle.setMap(null); - } - doCreateRectangle({ definition, }) { - const { northEast, southWest, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const rectangle = new _google.maps.Rectangle({ - bounds: new _google.maps.LatLngBounds(southWest, northEast), - map: this.map, - ...rawOptions, - ...bridgeOptions, - }); - if (title) { - rectangle.set('title', title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: rectangle }); - } - return rectangle; - } - doRemoveRectangle(rectangle) { - rectangle.setMap(null); - } - doCreateInfoWindow({ definition, element, }) { - const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; - let position = null; - if (element instanceof google.maps.Circle) { - position = element.getCenter(); - } - else if (element instanceof google.maps.Rectangle) { - position = element.getBounds()?.getCenter() || null; - } - else if (element instanceof google.maps.Polygon || element instanceof google.maps.Polyline) ; - const infoWindowOptions = { - headerContent: this.createTextOrElement(headerContent), - content: this.createTextOrElement(content), - position, - ...rawOptions, - ...bridgeOptions, - }; - const infoWindow = new _google.maps.InfoWindow(infoWindowOptions); - element.addListener('click', (event) => { - if (autoClose) { - this.closeInfoWindowsExcept(infoWindow); - } - if (infoWindowOptions.position === null) { - infoWindow.setPosition(event.latLng); - } - infoWindow.open({ map: this.map, anchor: element }); - }); - if (opened) { - if (autoClose) { - this.closeInfoWindowsExcept(infoWindow); - } - infoWindow.open({ map: this.map, anchor: element }); - } - return infoWindow; - } - doFitBoundsToMarkers() { - if (this.markers.size === 0) { - return; - } - const bounds = new google.maps.LatLngBounds(); - this.markers.forEach((marker) => { - if (!marker.position) { - return; - } - bounds.extend(marker.position); - }); - this.map.fitBounds(bounds); - } - createTextOrElement(content) { - if (!content) { - return null; - } - if (content.includes('<')) { - const div = document.createElement('div'); - div.innerHTML = content; - return div; - } - return content; - } - doCreateIcon({ definition, element }) { - const { type, width, height } = definition; - if (type === IconTypes.Svg) { - element.content = parser.parseFromString(definition.html, 'image/svg+xml').documentElement; - } - else if (type === IconTypes.UxIcon) { - element.content = parser.parseFromString(definition._generated_html, 'image/svg+xml').documentElement; - } - else if (type === IconTypes.Url) { - const icon = document.createElement('img'); - icon.width = width; - icon.height = height; - icon.src = definition.url; - element.content = icon; - } - else { - throw new Error(`Unsupported icon type: ${type}.`); - } - } - closeInfoWindowsExcept(infoWindow) { - this.infoWindows.forEach((otherInfoWindow) => { - if (otherInfoWindow !== infoWindow) { - otherInfoWindow.close(); - } - }); - } -} - -export { map_controller as default }; +// src/map_controller.ts +var _google; +var _loading = false; +var _loaded = false; +var _onLoadedCallbacks = []; +var parser = new DOMParser(); +var map_controller_default = class extends abstract_map_controller_default { + async connect() { + const onLoaded = () => super.connect(); + if (_loaded) { + onLoaded(); + return; + } + if (_loading) { + _onLoadedCallbacks.push(onLoaded); + return; + } + _loading = true; + _google = { maps: {} }; + let { libraries = [], ...loaderOptions } = this.providerOptionsValue; + const loader = new Loader(loaderOptions); + libraries = ["core", ...libraries.filter((library) => library !== "core")]; + const librariesImplementations = await Promise.all(libraries.map((library) => loader.importLibrary(library))); + librariesImplementations.map((libraryImplementation, index) => { + if (typeof libraryImplementation !== "object" || libraryImplementation === null) { + return; + } + const library = libraries[index]; + if (["marker", "places", "geometry", "journeySharing", "drawing", "visualization"].includes(library)) { + _google.maps[library] = libraryImplementation; + } else { + _google.maps = { ..._google.maps, ...libraryImplementation }; + } + }); + _loading = false; + _loaded = true; + onLoaded(); + _onLoadedCallbacks.forEach((callback) => callback()); + _onLoadedCallbacks = []; + } + centerValueChanged() { + if (this.map && this.hasCenterValue && this.centerValue) { + this.map.setCenter(this.centerValue); + } + } + zoomValueChanged() { + if (this.map && this.hasZoomValue && this.zoomValue) { + this.map.setZoom(this.zoomValue); + } + } + minZoomValueChanged() { + if (this.map && this.hasMinZoomValue && this.minZoomValue) { + this.map.setOptions({ minZoom: this.minZoomValue }); + } + } + maxZoomValueChanged() { + if (this.map && this.hasMaxZoomValue && this.maxZoomValue) { + this.map.setOptions({ maxZoom: this.maxZoomValue }); + } + } + dispatchEvent(name, payload = {}) { + payload.google = _google; + this.dispatch(name, { + prefix: "ux:map", + detail: payload + }); + } + doCreateMap({ definition }) { + const { center, zoom, minZoom, maxZoom, options, bridgeOptions = {} } = definition; + options.zoomControl = typeof options.zoomControlOptions !== "undefined"; + options.mapTypeControl = typeof options.mapTypeControlOptions !== "undefined"; + options.streetViewControl = typeof options.streetViewControlOptions !== "undefined"; + options.fullscreenControl = typeof options.fullscreenControlOptions !== "undefined"; + return new _google.maps.Map(this.element, { + center, + zoom, + minZoom, + maxZoom, + ...options, + ...bridgeOptions + }); + } + doCreateMarker({ + definition + }) { + const { "@id": _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; + const marker = new _google.maps.marker.AdvancedMarkerElement({ + position, + title, + map: this.map, + ...rawOptions, + ...bridgeOptions + }); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: marker }); + } + if (icon) { + this.doCreateIcon({ definition: icon, element: marker }); + } + return marker; + } + doRemoveMarker(marker) { + marker.map = null; + } + doCreatePolygon({ + definition + }) { + const { "@id": _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const polygon = new _google.maps.Polygon({ + paths: points, + map: this.map, + ...rawOptions, + ...bridgeOptions + }); + if (title) { + polygon.set("title", title); + } + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polygon }); + } + return polygon; + } + doRemovePolygon(polygon) { + polygon.setMap(null); + } + doCreatePolyline({ + definition + }) { + const { "@id": _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const polyline = new _google.maps.Polyline({ + path: points, + map: this.map, + ...rawOptions, + ...bridgeOptions + }); + if (title) { + polyline.set("title", title); + } + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polyline }); + } + return polyline; + } + doRemovePolyline(polyline) { + polyline.setMap(null); + } + doCreateCircle({ definition }) { + const { "@id": _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const circle = new _google.maps.Circle({ + center, + radius, + map: this.map, + ...rawOptions, + ...bridgeOptions + }); + if (title) { + circle.set("title", title); + } + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: circle }); + } + return circle; + } + doRemoveCircle(circle) { + circle.setMap(null); + } + doCreateRectangle({ + definition + }) { + const { northEast, southWest, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const rectangle = new _google.maps.Rectangle({ + bounds: new _google.maps.LatLngBounds(southWest, northEast), + map: this.map, + ...rawOptions, + ...bridgeOptions + }); + if (title) { + rectangle.set("title", title); + } + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: rectangle }); + } + return rectangle; + } + doRemoveRectangle(rectangle) { + rectangle.setMap(null); + } + doCreateInfoWindow({ + definition, + element + }) { + const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; + let position = null; + if (element instanceof google.maps.Circle) { + position = element.getCenter(); + } else if (element instanceof google.maps.Rectangle) { + position = element.getBounds()?.getCenter() || null; + } else if (element instanceof google.maps.Polygon || element instanceof google.maps.Polyline) { + } + const infoWindowOptions = { + headerContent: this.createTextOrElement(headerContent), + content: this.createTextOrElement(content), + position, + ...rawOptions, + ...bridgeOptions + }; + const infoWindow = new _google.maps.InfoWindow(infoWindowOptions); + element.addListener("click", (event) => { + if (autoClose) { + this.closeInfoWindowsExcept(infoWindow); + } + if (infoWindowOptions.position === null) { + infoWindow.setPosition(event.latLng); + } + infoWindow.open({ map: this.map, anchor: element }); + }); + if (opened) { + if (autoClose) { + this.closeInfoWindowsExcept(infoWindow); + } + infoWindow.open({ map: this.map, anchor: element }); + } + return infoWindow; + } + doFitBoundsToMarkers() { + if (this.markers.size === 0) { + return; + } + const bounds = new google.maps.LatLngBounds(); + this.markers.forEach((marker) => { + if (!marker.position) { + return; + } + bounds.extend(marker.position); + }); + this.map.fitBounds(bounds); + } + createTextOrElement(content) { + if (!content) { + return null; + } + if (content.includes("<")) { + const div = document.createElement("div"); + div.innerHTML = content; + return div; + } + return content; + } + doCreateIcon({ definition, element }) { + const { type, width, height } = definition; + if (type === IconTypes.Svg) { + element.content = parser.parseFromString(definition.html, "image/svg+xml").documentElement; + } else if (type === IconTypes.UxIcon) { + element.content = parser.parseFromString(definition._generated_html, "image/svg+xml").documentElement; + } else if (type === IconTypes.Url) { + const icon = document.createElement("img"); + icon.width = width; + icon.height = height; + icon.src = definition.url; + element.content = icon; + } else { + throw new Error(`Unsupported icon type: ${type}.`); + } + } + closeInfoWindowsExcept(infoWindow) { + this.infoWindows.forEach((otherInfoWindow) => { + if (otherInfoWindow !== infoWindow) { + otherInfoWindow.close(); + } + }); + } +}; +export { + map_controller_default as default +}; diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts index 4cb60349dda..3a27c9a869b 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts @@ -1,9 +1,8 @@ -import type { CircleDefinition, Icon, InfoWindowDefinition, MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map'; -import AbstractMapController from '@symfony/ux-map'; -import 'leaflet/dist/leaflet.min.css'; -import type { CircleOptions, ControlPosition, MapOptions as LeafletMapOptions, MarkerOptions, PolylineOptions as PolygonOptions, PolylineOptions, PopupOptions, PolylineOptions as RectangleOptions } from 'leaflet'; +import AbstractMapController, { MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, CircleDefinition, RectangleDefinition, InfoWindowDefinition, Icon } from '@symfony/ux-map'; import * as L from 'leaflet'; -type MapOptions = Pick & { +import { MapOptions as MapOptions$1, ControlPosition, MarkerOptions, PopupOptions, PolylineOptions, CircleOptions } from 'leaflet'; + +type MapOptions = Pick & { attributionControlOptions?: { position: ControlPosition; prefix: string | false; @@ -21,7 +20,7 @@ type MapOptions = Pick options: Record; } | false; }; -export default class extends AbstractMapController { +declare class export_default extends AbstractMapController { map: L.Map; connect(): void; centerValueChanged(): void; @@ -30,14 +29,14 @@ export default class extends AbstractMapController): void; protected doCreateMap({ definition }: { - definition: MapDefinition; + definition: MapDefinition; }): L.Map; protected doCreateMarker({ definition }: { definition: MarkerDefinition; }): L.Marker; protected doRemoveMarker(marker: L.Marker): void; protected doCreatePolygon({ definition }: { - definition: PolygonDefinition; + definition: PolygonDefinition; }): L.Polygon; protected doRemovePolygon(polygon: L.Polygon): void; protected doCreatePolyline({ definition }: { @@ -49,7 +48,7 @@ export default class extends AbstractMapController; + definition: RectangleDefinition; }): L.Rectangle; protected doRemoveRectangle(rectangle: L.Rectangle): void; protected doCreateInfoWindow({ definition, element, }: { @@ -63,4 +62,5 @@ export default class extends AbstractMapController this.createMarker({ definition })); - this.polygonsValue.forEach((definition) => this.createPolygon({ definition })); - this.polylinesValue.forEach((definition) => this.createPolyline({ definition })); - this.circlesValue.forEach((definition) => this.createCircle({ definition })); - this.rectanglesValue.forEach((definition) => this.createRectangle({ definition })); - if (this.fitBoundsToMarkersValue) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: [...this.markers.values()], - polygons: [...this.polygons.values()], - polylines: [...this.polylines.values()], - circles: [...this.circles.values()], - rectangles: [...this.rectangles.values()], - infoWindows: this.infoWindows, - extra, - }); - this.isConnected = true; - } - createInfoWindow({ definition, element, }) { - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, definition, element }); - this.infoWindows.push(infoWindow); - return infoWindow; - } - markersValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker); - if (this.fitBoundsToMarkersValue) { - this.doFitBoundsToMarkers(); - } +var abstract_map_controller_default = class extends Controller { + constructor() { + super(...arguments); + this.markers = /* @__PURE__ */ new Map(); + this.polygons = /* @__PURE__ */ new Map(); + this.polylines = /* @__PURE__ */ new Map(); + this.circles = /* @__PURE__ */ new Map(); + this.rectangles = /* @__PURE__ */ new Map(); + this.infoWindows = []; + this.isConnected = false; + } + connect() { + const extra = this.hasExtraValue ? this.extraValue : {}; + const mapDefinition = { + center: this.hasCenterValue ? this.centerValue : null, + zoom: this.hasZoomValue ? this.zoomValue : null, + minZoom: this.hasMinZoomValue ? this.minZoomValue : null, + maxZoom: this.hasMaxZoomValue ? this.maxZoomValue : null, + options: this.optionsValue, + extra + }; + this.dispatchEvent("pre-connect", mapDefinition); + this.createMarker = this.createDrawingFactory("marker", this.markers, this.doCreateMarker.bind(this)); + this.createPolygon = this.createDrawingFactory("polygon", this.polygons, this.doCreatePolygon.bind(this)); + this.createPolyline = this.createDrawingFactory("polyline", this.polylines, this.doCreatePolyline.bind(this)); + this.createCircle = this.createDrawingFactory("circle", this.circles, this.doCreateCircle.bind(this)); + this.createRectangle = this.createDrawingFactory("rectangle", this.rectangles, this.doCreateRectangle.bind(this)); + this.map = this.doCreateMap({ definition: mapDefinition }); + this.markersValue.forEach((definition) => this.createMarker({ definition })); + this.polygonsValue.forEach((definition) => this.createPolygon({ definition })); + this.polylinesValue.forEach((definition) => this.createPolyline({ definition })); + this.circlesValue.forEach((definition) => this.createCircle({ definition })); + this.rectanglesValue.forEach((definition) => this.createRectangle({ definition })); + if (this.fitBoundsToMarkersValue) { + this.doFitBoundsToMarkers(); } - polygonsValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon); + this.dispatchEvent("connect", { + map: this.map, + markers: [...this.markers.values()], + polygons: [...this.polygons.values()], + polylines: [...this.polylines.values()], + circles: [...this.circles.values()], + rectangles: [...this.rectangles.values()], + infoWindows: this.infoWindows, + extra + }); + this.isConnected = true; + } + //region Public API + createInfoWindow({ + definition, + element + }) { + this.dispatchEvent("info-window:before-create", { definition, element }); + const infoWindow = this.doCreateInfoWindow({ definition, element }); + this.dispatchEvent("info-window:after-create", { infoWindow, definition, element }); + this.infoWindows.push(infoWindow); + return infoWindow; + } + markersValueChanged() { + if (!this.isConnected) { + return; } - polylinesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline); + this.onDrawChanged(this.markers, this.markersValue, this.createMarker, this.doRemoveMarker); + if (this.fitBoundsToMarkersValue) { + this.doFitBoundsToMarkers(); } - circlesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle); + } + polygonsValueChanged() { + if (!this.isConnected) { + return; } - rectanglesValueChanged() { - if (!this.isConnected) { - return; - } - this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle); + this.onDrawChanged(this.polygons, this.polygonsValue, this.createPolygon, this.doRemovePolygon); + } + polylinesValueChanged() { + if (!this.isConnected) { + return; } - createDrawingFactory(type, draws, factory) { - const eventBefore = `${type}:before-create`; - const eventAfter = `${type}:after-create`; - return ({ definition }) => { - this.dispatchEvent(eventBefore, { definition }); - if (typeof definition.rawOptions !== 'undefined') { - console.warn(`[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, definition); - } - const drawing = factory({ definition }); - this.dispatchEvent(eventAfter, { [type]: drawing, definition }); - draws.set(definition['@id'], drawing); - return drawing; - }; + this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline); + } + circlesValueChanged() { + if (!this.isConnected) { + return; } - onDrawChanged(draws, newDrawDefinitions, factory, remover) { - const idsToRemove = new Set(draws.keys()); - newDrawDefinitions.forEach((definition) => { - idsToRemove.delete(definition['@id']); - }); - idsToRemove.forEach((id) => { - const draw = draws.get(id); - remover(draw); - draws.delete(id); - }); - newDrawDefinitions.forEach((definition) => { - if (!draws.has(definition['@id'])) { - factory({ definition }); - } - }); + this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle); + } + rectanglesValueChanged() { + if (!this.isConnected) { + return; } -} -default_1.values = { - providerOptions: Object, - center: Object, - zoom: Number, - minZoom: Number, - maxZoom: Number, - fitBoundsToMarkers: Boolean, - markers: Array, - polygons: Array, - polylines: Array, - circles: Array, - rectangles: Array, - options: Object, - extra: Object, + this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle); + } + createDrawingFactory(type, draws, factory) { + const eventBefore = `${type}:before-create`; + const eventAfter = `${type}:after-create`; + return ({ definition }) => { + this.dispatchEvent(eventBefore, { definition }); + if (typeof definition.rawOptions !== "undefined") { + console.warn( + `[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, + definition + ); + } + const drawing = factory({ definition }); + this.dispatchEvent(eventAfter, { [type]: drawing, definition }); + draws.set(definition["@id"], drawing); + return drawing; + }; + } + onDrawChanged(draws, newDrawDefinitions, factory, remover) { + const idsToRemove = new Set(draws.keys()); + newDrawDefinitions.forEach((definition) => { + idsToRemove.delete(definition["@id"]); + }); + idsToRemove.forEach((id) => { + const draw = draws.get(id); + remover(draw); + draws.delete(id); + }); + newDrawDefinitions.forEach((definition) => { + if (!draws.has(definition["@id"])) { + factory({ definition }); + } + }); + } + //endregion +}; +abstract_map_controller_default.values = { + providerOptions: Object, + center: Object, + zoom: Number, + minZoom: Number, + maxZoom: Number, + fitBoundsToMarkers: Boolean, + markers: Array, + polygons: Array, + polylines: Array, + circles: Array, + rectangles: Array, + options: Object, + extra: Object }; -class map_controller extends default_1 { - connect() { - L.Marker.prototype.options.icon = L.divIcon({ - html: '', - iconSize: [25, 41], - iconAnchor: [12.5, 41], - popupAnchor: [0, -41], - className: '', - }); - super.connect(); +// src/map_controller.ts +import "leaflet/dist/leaflet.min.css"; +import * as L from "leaflet"; +var map_controller_default = class extends abstract_map_controller_default { + connect() { + L.Marker.prototype.options.icon = L.divIcon({ + html: '', + iconSize: [25, 41], + iconAnchor: [12.5, 41], + popupAnchor: [0, -41], + className: "" + // Adding an empty class to the icon to avoid the default Leaflet styles + }); + super.connect(); + } + centerValueChanged() { + if (this.map && this.hasCenterValue && this.centerValue && this.hasZoomValue && this.zoomValue) { + this.map.setView(this.centerValue, this.zoomValue); } - centerValueChanged() { - if (this.map && this.hasCenterValue && this.centerValue && this.hasZoomValue && this.zoomValue) { - this.map.setView(this.centerValue, this.zoomValue); - } + } + zoomValueChanged() { + if (this.map && this.hasZoomValue && this.zoomValue) { + this.map.setZoom(this.zoomValue); } - zoomValueChanged() { - if (this.map && this.hasZoomValue && this.zoomValue) { - this.map.setZoom(this.zoomValue); - } + } + minZoomValueChanged() { + if (this.map && this.hasMinZoomValue && this.minZoomValue) { + this.map.setMinZoom(this.minZoomValue); } - minZoomValueChanged() { - if (this.map && this.hasMinZoomValue && this.minZoomValue) { - this.map.setMinZoom(this.minZoomValue); - } + } + maxZoomValueChanged() { + if (this.map && this.hasMaxZoomValue && this.maxZoomValue) { + this.map.setMaxZoom(this.maxZoomValue); } - maxZoomValueChanged() { - if (this.map && this.hasMaxZoomValue && this.maxZoomValue) { - this.map.setMaxZoom(this.maxZoomValue); - } + } + dispatchEvent(name, payload = {}) { + payload.L = L; + this.dispatch(name, { + prefix: "ux:map", + detail: payload + }); + } + doCreateMap({ definition }) { + const { center, zoom, minZoom, maxZoom, options, bridgeOptions = {} } = definition; + const map2 = L.map(this.element, { + center: center === null ? void 0 : center, + zoom: zoom === null ? void 0 : zoom, + minZoom: minZoom === null ? void 0 : minZoom, + maxZoom: maxZoom === null ? void 0 : maxZoom, + attributionControl: false, + zoomControl: false, + ...options, + ...bridgeOptions + }); + if (options.tileLayer) { + L.tileLayer(options.tileLayer.url, { + attribution: options.tileLayer.attribution, + ...options.tileLayer.options + }).addTo(map2); } - dispatchEvent(name, payload = {}) { - payload.L = L; - this.dispatch(name, { - prefix: 'ux:map', - detail: payload, - }); + if (typeof options.attributionControlOptions !== "undefined") { + L.control.attribution({ ...options.attributionControlOptions }).addTo(map2); } - doCreateMap({ definition }) { - const { center, zoom, minZoom, maxZoom, options, bridgeOptions = {} } = definition; - const map = L.map(this.element, { - center: center === null ? undefined : center, - zoom: zoom === null ? undefined : zoom, - minZoom: minZoom === null ? undefined : minZoom, - maxZoom: maxZoom === null ? undefined : maxZoom, - attributionControl: false, - zoomControl: false, - ...options, - ...bridgeOptions, - }); - if (options.tileLayer) { - L.tileLayer(options.tileLayer.url, { - attribution: options.tileLayer.attribution, - ...options.tileLayer.options, - }).addTo(map); - } - if (typeof options.attributionControlOptions !== 'undefined') { - L.control.attribution({ ...options.attributionControlOptions }).addTo(map); - } - if (typeof options.zoomControlOptions !== 'undefined') { - L.control.zoom({ ...options.zoomControlOptions }).addTo(map); - } - return map; + if (typeof options.zoomControlOptions !== "undefined") { + L.control.zoom({ ...options.zoomControlOptions }).addTo(map2); } - doCreateMarker({ definition }) { - const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; - const marker = L.marker(position, { - title: title || undefined, - ...rawOptions, - ...bridgeOptions, - riseOnHover: true, - }).addTo(this.map); - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: marker }); - } - if (icon) { - this.doCreateIcon({ definition: icon, element: marker }); - } - return marker; + return map2; + } + doCreateMarker({ definition }) { + const { "@id": _id, position, title, infoWindow, icon: icon2, rawOptions = {}, bridgeOptions = {} } = definition; + const marker2 = L.marker(position, { + title: title || void 0, + ...rawOptions, + ...bridgeOptions, + riseOnHover: true + }).addTo(this.map); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: marker2 }); } - doRemoveMarker(marker) { - marker.remove(); + if (icon2) { + this.doCreateIcon({ definition: icon2, element: marker2 }); } - doCreatePolygon({ definition }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const polygon = L.polygon(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); - if (title) { - polygon.bindPopup(title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: polygon }); - } - return polygon; + return marker2; + } + doRemoveMarker(marker2) { + marker2.remove(); + } + doCreatePolygon({ definition }) { + const { "@id": _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const polygon2 = L.polygon(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); + if (title) { + polygon2.bindPopup(title); } - doRemovePolygon(polygon) { - polygon.remove(); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polygon2 }); } - doCreatePolyline({ definition }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const polyline = L.polyline(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); - if (title) { - polyline.bindPopup(title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: polyline }); - } - return polyline; + return polygon2; + } + doRemovePolygon(polygon2) { + polygon2.remove(); + } + doCreatePolyline({ definition }) { + const { "@id": _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const polyline2 = L.polyline(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); + if (title) { + polyline2.bindPopup(title); } - doRemovePolyline(polyline) { - polyline.remove(); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polyline2 }); } - doCreateCircle({ definition }) { - const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const circle = L.circle(center, { radius, ...rawOptions, ...bridgeOptions }).addTo(this.map); - if (title) { - circle.bindPopup(title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: circle }); - } - return circle; + return polyline2; + } + doRemovePolyline(polyline2) { + polyline2.remove(); + } + doCreateCircle({ definition }) { + const { "@id": _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const circle2 = L.circle(center, { radius, ...rawOptions, ...bridgeOptions }).addTo(this.map); + if (title) { + circle2.bindPopup(title); } - doRemoveCircle(circle) { - circle.remove(); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: circle2 }); } - doCreateRectangle({ definition }) { - const { '@id': _id, southWest, northEast, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const rectangle = L.rectangle([ - [southWest.lat, southWest.lng], - [northEast.lat, northEast.lng], - ], { ...rawOptions, ...bridgeOptions }).addTo(this.map); - if (title) { - rectangle.bindPopup(title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: rectangle }); - } - return rectangle; + return circle2; + } + doRemoveCircle(circle2) { + circle2.remove(); + } + doCreateRectangle({ definition }) { + const { "@id": _id, southWest, northEast, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const rectangle2 = L.rectangle( + [ + [southWest.lat, southWest.lng], + [northEast.lat, northEast.lng] + ], + { ...rawOptions, ...bridgeOptions } + ).addTo(this.map); + if (title) { + rectangle2.bindPopup(title); } - doRemoveRectangle(rectangle) { - rectangle.remove(); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: rectangle2 }); } - doCreateInfoWindow({ definition, element, }) { - const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; - element.bindPopup([headerContent, content].filter((x) => x).join(''), { ...rawOptions, ...bridgeOptions }); - if (opened) { - if (autoClose) { - this.closePopups(); - } - element.openPopup(); - } - const popup = element.getPopup(); - if (!popup) { - throw new Error('Unable to get the Popup associated with the element.'); - } - popup.on('click', () => { - if (autoClose) { - this.closePopups({ except: popup }); - } - }); - return popup; + return rectangle2; + } + doRemoveRectangle(rectangle2) { + rectangle2.remove(); + } + doCreateInfoWindow({ + definition, + element + }) { + const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; + element.bindPopup([headerContent, content].filter((x) => x).join(""), { ...rawOptions, ...bridgeOptions }); + if (opened) { + if (autoClose) { + this.closePopups(); + } + element.openPopup(); } - doCreateIcon({ definition, element }) { - const { type, width, height } = definition; - let icon; - if (type === IconTypes.Svg) { - icon = L.divIcon({ - html: definition.html, - iconSize: [width, height], - className: '', - }); - } - else if (type === IconTypes.UxIcon) { - icon = L.divIcon({ - html: definition._generated_html, - iconSize: [width, height], - className: '', - }); - } - else if (type === IconTypes.Url) { - icon = L.icon({ - iconUrl: definition.url, - iconSize: [width, height], - className: '', - }); - } - else { - throw new Error(`Unsupported icon type: ${type}.`); - } - element.setIcon(icon); + const popup = element.getPopup(); + if (!popup) { + throw new Error("Unable to get the Popup associated with the element."); } - doFitBoundsToMarkers() { - if (this.markers.size === 0) { - return; - } - const bounds = []; - this.markers.forEach((marker) => { - const position = marker.getLatLng(); - bounds.push([position.lat, position.lng]); - }); - this.map.fitBounds(bounds); + popup.on("click", () => { + if (autoClose) { + this.closePopups({ except: popup }); + } + }); + return popup; + } + doCreateIcon({ definition, element }) { + const { type, width, height } = definition; + let icon2; + if (type === IconTypes.Svg) { + icon2 = L.divIcon({ + html: definition.html, + iconSize: [width, height], + className: "" + // Adding an empty class to the icon to avoid the default Leaflet styles + }); + } else if (type === IconTypes.UxIcon) { + icon2 = L.divIcon({ + html: definition._generated_html, + iconSize: [width, height], + className: "" + // Adding an empty class to the icon to avoid the default Leaflet styles + }); + } else if (type === IconTypes.Url) { + icon2 = L.icon({ + iconUrl: definition.url, + iconSize: [width, height], + className: "" + // Adding an empty class to the icon to avoid the default Leaflet styles + }); + } else { + throw new Error(`Unsupported icon type: ${type}.`); } - closePopups(options = {}) { - this.infoWindows.forEach((popup) => { - if (options.except && popup === options.except) { - return; - } - popup.close(); - }); + element.setIcon(icon2); + } + doFitBoundsToMarkers() { + if (this.markers.size === 0) { + return; } -} - -export { map_controller as default }; + const bounds = []; + this.markers.forEach((marker2) => { + const position = marker2.getLatLng(); + bounds.push([position.lat, position.lng]); + }); + this.map.fitBounds(bounds); + } + closePopups(options = {}) { + this.infoWindows.forEach((popup) => { + if (options.except && popup === options.except) { + return; + } + popup.close(); + }); + } +}; +export { + map_controller_default as default +}; diff --git a/src/Notify/assets/dist/controller.d.ts b/src/Notify/assets/dist/controller.d.ts index e9252002f3c..ba357910892 100644 --- a/src/Notify/assets/dist/controller.d.ts +++ b/src/Notify/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { static values: { hub: StringConstructor; topics: ArrayConstructor; @@ -16,3 +17,5 @@ export default class extends Controller { _notify(title: string | undefined, options: NotificationOptions | undefined): void; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/Notify/assets/dist/controller.js b/src/Notify/assets/dist/controller.js index a69ae1a254b..12f2fe62523 100644 --- a/src/Notify/assets/dist/controller.js +++ b/src/Notify/assets/dist/controller.js @@ -1,72 +1,69 @@ -import { Controller } from '@hotwired/stimulus'; - -class default_1 extends Controller { - constructor() { - super(...arguments); - this.eventSources = []; - this.listeners = new WeakMap(); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +var controller_default = class extends Controller { + constructor() { + super(...arguments); + this.eventSources = []; + this.listeners = /* @__PURE__ */ new WeakMap(); + } + initialize() { + const errorMessages = []; + if (!this.hasHubValue) errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.'); + if (!this.hasTopicsValue) errorMessages.push('A "topics" value must be provided.'); + if (errorMessages.length) throw new Error(errorMessages.join(" ")); + this.eventSources = this.topicsValue.map((topic) => { + const u = new URL(this.hubValue); + u.searchParams.append("topic", topic); + return new EventSource(u); + }); + } + connect() { + if (!("Notification" in window)) { + console.warn("This browser does not support desktop notifications."); + return; } - initialize() { - const errorMessages = []; - if (!this.hasHubValue) - errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.'); - if (!this.hasTopicsValue) - errorMessages.push('A "topics" value must be provided.'); - if (errorMessages.length) - throw new Error(errorMessages.join(' ')); - this.eventSources = this.topicsValue.map((topic) => { - const u = new URL(this.hubValue); - u.searchParams.append('topic', topic); - return new EventSource(u); - }); + this.eventSources.forEach((eventSource) => { + const listener = (event) => { + const { summary, content } = JSON.parse(event.data); + this._notify(summary, content); + }; + eventSource.addEventListener("message", listener); + this.listeners.set(eventSource, listener); + }); + this.dispatchEvent("connect", { eventSources: this.eventSources }); + } + disconnect() { + this.eventSources.forEach((eventSource) => { + const listener = this.listeners.get(eventSource); + if (listener) { + eventSource.removeEventListener("message", listener); + } + eventSource.close(); + }); + this.eventSources = []; + } + _notify(title, options) { + if (!title) return; + if ("granted" === Notification.permission) { + new Notification(title, options); + return; } - connect() { - if (!('Notification' in window)) { - console.warn('This browser does not support desktop notifications.'); - return; + if ("denied" !== Notification.permission) { + Notification.requestPermission().then((permission) => { + if ("granted" === permission) { + new Notification(title, options); } - this.eventSources.forEach((eventSource) => { - const listener = (event) => { - const { summary, content } = JSON.parse(event.data); - this._notify(summary, content); - }; - eventSource.addEventListener('message', listener); - this.listeners.set(eventSource, listener); - }); - this.dispatchEvent('connect', { eventSources: this.eventSources }); + }); } - disconnect() { - this.eventSources.forEach((eventSource) => { - const listener = this.listeners.get(eventSource); - if (listener) { - eventSource.removeEventListener('message', listener); - } - eventSource.close(); - }); - this.eventSources = []; - } - _notify(title, options) { - if (!title) - return; - if ('granted' === Notification.permission) { - new Notification(title, options); - return; - } - if ('denied' !== Notification.permission) { - Notification.requestPermission().then((permission) => { - if ('granted' === permission) { - new Notification(title, options); - } - }); - } - } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'notify' }); - } -} -default_1.values = { - hub: String, - topics: Array, + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "notify" }); + } +}; +controller_default.values = { + hub: String, + topics: Array +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/React/CHANGELOG.md b/src/React/CHANGELOG.md index da9a6a6bd36..1f3ff553f1f 100644 --- a/src/React/CHANGELOG.md +++ b/src/React/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGELOG +## 2.28.0 + +- [BC BREAK] By modernizing our building tools, the file `dist/render_controller.js` now does not contain any useless + code related to `development` environment. + + This file is now smaller and faster to load, but the imported module changed from `react-dom` to `react-dom/client`: + - You **are not impacted** if you are using the Symfony AssetMapper and Symfony Flex, or Webpack Encore. + - You **are impacted** if you are using the Symfony AssetMapper but **not** Symfony Flex, you need to : + ```shell + php bin/console importmap:remove react-dom + php bin/console importmap:require react-dom + ``` + ## 2.26.0 - Improve error handling when resolving a React component diff --git a/src/React/assets/dist/components.d.ts b/src/React/assets/dist/components.d.ts index 88860e39b07..fed706aca42 100644 --- a/src/React/assets/dist/components.d.ts +++ b/src/React/assets/dist/components.d.ts @@ -1,7 +1,9 @@ -import type { ComponentClass, FunctionComponent } from 'react'; +import { FunctionComponent, ComponentClass } from 'react'; + type Component = string | FunctionComponent | ComponentClass; -export interface ComponentCollection { +interface ComponentCollection { [key: string]: Component; } -export declare const components: ComponentCollection; -export {}; +declare const components: ComponentCollection; + +export { type ComponentCollection, components }; diff --git a/src/React/assets/dist/components.js b/src/React/assets/dist/components.js index 2ea8342f45d..7973d4ddfa5 100644 --- a/src/React/assets/dist/components.js +++ b/src/React/assets/dist/components.js @@ -1,3 +1,4 @@ const components = {}; - -export { components }; +export { + components +}; diff --git a/src/React/assets/dist/loader.d.ts b/src/React/assets/dist/loader.d.ts index 109970c9fc5..6c615ace696 100644 --- a/src/React/assets/dist/loader.d.ts +++ b/src/React/assets/dist/loader.d.ts @@ -1,5 +1,6 @@ -import type { ComponentClass, FunctionComponent } from 'react'; -import { type ComponentCollection } from './components.js'; +import { FunctionComponent, ComponentClass } from 'react'; +import { ComponentCollection } from './components.js'; + type Component = string | FunctionComponent | ComponentClass; declare global { function resolveReactComponent(name: string): Component; @@ -7,5 +8,6 @@ declare global { resolveReactComponent(name: string): Component; } } -export declare function registerReactControllerComponents(reactComponents?: ComponentCollection): void; -export {}; +declare function registerReactControllerComponents(reactComponents?: ComponentCollection): void; + +export { registerReactControllerComponents }; diff --git a/src/React/assets/dist/loader.js b/src/React/assets/dist/loader.js index 6fcf0bf9776..c460e467450 100644 --- a/src/React/assets/dist/loader.js +++ b/src/React/assets/dist/loader.js @@ -1,14 +1,14 @@ -import { components } from './components.js'; - +import { components } from "./components.js"; function registerReactControllerComponents(reactComponents = components) { - window.resolveReactComponent = (name) => { - const component = reactComponents[name]; - if (typeof component === 'undefined') { - const possibleValues = Object.keys(reactComponents).length > 0 ? Object.keys(reactComponents).join(', ') : 'none'; - throw new Error(`React controller "${name}" does not exist. Possible values: ${possibleValues}`); - } - return component; - }; + window.resolveReactComponent = (name) => { + const component = reactComponents[name]; + if (typeof component === "undefined") { + const possibleValues = Object.keys(reactComponents).length > 0 ? Object.keys(reactComponents).join(", ") : "none"; + throw new Error(`React controller "${name}" does not exist. Possible values: ${possibleValues}`); + } + return component; + }; } - -export { registerReactControllerComponents }; +export { + registerReactControllerComponents +}; diff --git a/src/React/assets/dist/register_controller.d.ts b/src/React/assets/dist/register_controller.d.ts index 7743864c682..2669928769e 100644 --- a/src/React/assets/dist/register_controller.d.ts +++ b/src/React/assets/dist/register_controller.d.ts @@ -1,4 +1,5 @@ -import type { ComponentClass, FunctionComponent } from 'react'; +import { FunctionComponent, ComponentClass } from 'react'; + type Component = string | FunctionComponent | ComponentClass; declare global { function resolveReactComponent(name: string): Component; @@ -6,5 +7,6 @@ declare global { resolveReactComponent(name: string): Component; } } -export declare function registerReactControllerComponents(context: __WebpackModuleApi.RequireContext): void; -export {}; +declare function registerReactControllerComponents(context: __WebpackModuleApi.RequireContext): void; + +export { registerReactControllerComponents }; diff --git a/src/React/assets/dist/register_controller.js b/src/React/assets/dist/register_controller.js index d0c38da6a83..85d4c4b2b9e 100644 --- a/src/React/assets/dist/register_controller.js +++ b/src/React/assets/dist/register_controller.js @@ -1,23 +1,26 @@ function registerReactControllerComponents(context) { - const reactControllers = {}; - const importAllReactComponents = (r) => { - r.keys().forEach((key) => { - reactControllers[key] = r(key).default; - }); - }; - importAllReactComponents(context); - window.resolveReactComponent = (name) => { - const component = reactControllers[`./${name}.jsx`] || reactControllers[`./${name}.tsx`]; - if (typeof component === 'undefined') { - const possibleValues = Object.keys(reactControllers).map((key) => key.replace('./', '').replace('.jsx', '').replace('.tsx', '')); - if (possibleValues.includes(name)) { - throw new Error(` + const reactControllers = {}; + const importAllReactComponents = (r) => { + r.keys().forEach((key) => { + reactControllers[key] = r(key).default; + }); + }; + importAllReactComponents(context); + window.resolveReactComponent = (name) => { + const component = reactControllers[`./${name}.jsx`] || reactControllers[`./${name}.tsx`]; + if (typeof component === "undefined") { + const possibleValues = Object.keys(reactControllers).map( + (key) => key.replace("./", "").replace(".jsx", "").replace(".tsx", "") + ); + if (possibleValues.includes(name)) { + throw new Error(` React controller "${name}" could not be resolved. Ensure the module exports the controller as a default export.`); - } - throw new Error(`React controller "${name}" does not exist. Possible values: ${possibleValues.join(', ')}`); - } - return component; - }; + } + throw new Error(`React controller "${name}" does not exist. Possible values: ${possibleValues.join(", ")}`); + } + return component; + }; } - -export { registerReactControllerComponents }; +export { + registerReactControllerComponents +}; diff --git a/src/React/assets/dist/render_controller.d.ts b/src/React/assets/dist/render_controller.d.ts index f8f47bb98a9..0d92a6dead3 100644 --- a/src/React/assets/dist/render_controller.d.ts +++ b/src/React/assets/dist/render_controller.d.ts @@ -1,6 +1,7 @@ import { Controller } from '@hotwired/stimulus'; -import { type ReactElement } from 'react'; -export default class extends Controller { +import { ReactElement } from 'react'; + +declare class export_default extends Controller { readonly componentValue?: string; readonly propsValue?: object; readonly permanentValue: boolean; @@ -17,3 +18,5 @@ export default class extends Controller { _renderReactElement(reactElement: ReactElement): void; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/React/assets/dist/render_controller.js b/src/React/assets/dist/render_controller.js index 44dbaafcd2d..f1f7edb4567 100644 --- a/src/React/assets/dist/render_controller.js +++ b/src/React/assets/dist/render_controller.js @@ -1,83 +1,47 @@ -import { Controller } from '@hotwired/stimulus'; -import React from 'react'; -import require$$0 from 'react-dom'; - -var client = {}; - -var hasRequiredClient; - -function requireClient () { - if (hasRequiredClient) return client; - hasRequiredClient = 1; - - var m = require$$0; - if (process.env.NODE_ENV === 'production') { - client.createRoot = m.createRoot; - client.hydrateRoot = m.hydrateRoot; - } else { - var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - client.createRoot = function(c, o) { - i.usingClientEntryPoint = true; - try { - return m.createRoot(c, o); - } finally { - i.usingClientEntryPoint = false; - } - }; - client.hydrateRoot = function(c, h, o) { - i.usingClientEntryPoint = true; - try { - return m.hydrateRoot(c, h, o); - } finally { - i.usingClientEntryPoint = false; - } - }; - } - return client; -} - -var clientExports = requireClient(); - -class default_1 extends Controller { - connect() { - const props = this.propsValue ? this.propsValue : null; - this.dispatchEvent('connect', { component: this.componentValue, props: props }); - if (!this.componentValue) { - throw new Error('No component specified.'); - } - const component = window.resolveReactComponent(this.componentValue); - this._renderReactElement(React.createElement(component, props, null)); - this.dispatchEvent('mount', { - componentName: this.componentValue, - component: component, - props: props, - }); - } - disconnect() { - if (this.permanentValue) { - return; - } - this.element.root.unmount(); - this.dispatchEvent('unmount', { - component: this.componentValue, - props: this.propsValue ? this.propsValue : null, - }); +import { Controller } from "@hotwired/stimulus"; +import React from "react"; +import { createRoot } from "react-dom/client"; +class render_controller_default extends Controller { + connect() { + const props = this.propsValue ? this.propsValue : null; + this.dispatchEvent("connect", { component: this.componentValue, props }); + if (!this.componentValue) { + throw new Error("No component specified."); } - _renderReactElement(reactElement) { - const element = this.element; - if (!element.root) { - element.root = clientExports.createRoot(this.element); - } - element.root.render(reactElement); + const component = window.resolveReactComponent(this.componentValue); + this._renderReactElement(React.createElement(component, props, null)); + this.dispatchEvent("mount", { + componentName: this.componentValue, + component, + props + }); + } + disconnect() { + if (this.permanentValue) { + return; } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'react' }); + this.element.root.unmount(); + this.dispatchEvent("unmount", { + component: this.componentValue, + props: this.propsValue ? this.propsValue : null + }); + } + _renderReactElement(reactElement) { + const element = this.element; + if (!element.root) { + element.root = createRoot(this.element); } + element.root.render(reactElement); + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "react" }); + } } -default_1.values = { - component: String, - props: Object, - permanent: { type: Boolean, default: false }, +render_controller_default.values = { + component: String, + props: Object, + permanent: { type: Boolean, default: false } +}; +export { + render_controller_default as default }; - -export { default_1 as default }; diff --git a/src/StimulusBundle/assets/dist/controllers.d.ts b/src/StimulusBundle/assets/dist/controllers.d.ts index e7da29ccd08..5ece79acef8 100644 --- a/src/StimulusBundle/assets/dist/controllers.d.ts +++ b/src/StimulusBundle/assets/dist/controllers.d.ts @@ -1,12 +1,15 @@ -import type { ControllerConstructor } from '@hotwired/stimulus'; -export interface EagerControllersCollection { +import { ControllerConstructor } from '@hotwired/stimulus'; + +interface EagerControllersCollection { [key: string]: ControllerConstructor; } -export interface LazyControllersCollection { +interface LazyControllersCollection { [key: string]: () => Promise<{ default: ControllerConstructor; }>; } -export declare const eagerControllers: EagerControllersCollection; -export declare const lazyControllers: LazyControllersCollection; -export declare const isApplicationDebug = false; +declare const eagerControllers: EagerControllersCollection; +declare const lazyControllers: LazyControllersCollection; +declare const isApplicationDebug = false; + +export { type EagerControllersCollection, type LazyControllersCollection, eagerControllers, isApplicationDebug, lazyControllers }; diff --git a/src/StimulusBundle/assets/dist/controllers.js b/src/StimulusBundle/assets/dist/controllers.js index 2a111724b07..4e6a1858aaa 100644 --- a/src/StimulusBundle/assets/dist/controllers.js +++ b/src/StimulusBundle/assets/dist/controllers.js @@ -1,5 +1,8 @@ const eagerControllers = {}; const lazyControllers = {}; const isApplicationDebug = false; - -export { eagerControllers, isApplicationDebug, lazyControllers }; +export { + eagerControllers, + isApplicationDebug, + lazyControllers +}; diff --git a/src/StimulusBundle/assets/dist/loader.d.ts b/src/StimulusBundle/assets/dist/loader.d.ts index ffa119a7365..0354629d565 100644 --- a/src/StimulusBundle/assets/dist/loader.d.ts +++ b/src/StimulusBundle/assets/dist/loader.d.ts @@ -1,4 +1,7 @@ import { Application } from '@hotwired/stimulus'; -import { type EagerControllersCollection, type LazyControllersCollection } from './controllers.js'; -export declare const loadControllers: (application: Application, eagerControllers: EagerControllersCollection, lazyControllers: LazyControllersCollection) => void; -export declare const startStimulusApp: () => Application; +import { EagerControllersCollection, LazyControllersCollection } from './controllers.js'; + +declare const loadControllers: (application: Application, eagerControllers: EagerControllersCollection, lazyControllers: LazyControllersCollection) => void; +declare const startStimulusApp: () => Application; + +export { loadControllers, startStimulusApp }; diff --git a/src/StimulusBundle/assets/dist/loader.js b/src/StimulusBundle/assets/dist/loader.js index b2c49cfa2b7..7ca5fff0722 100644 --- a/src/StimulusBundle/assets/dist/loader.js +++ b/src/StimulusBundle/assets/dist/loader.js @@ -1,93 +1,98 @@ -import { Application } from '@hotwired/stimulus'; -import { isApplicationDebug, eagerControllers, lazyControllers } from './controllers.js'; - -const controllerAttribute = 'data-controller'; -const loadControllers = (application, eagerControllers, lazyControllers) => { - for (const name in eagerControllers) { - registerController(name, eagerControllers[name], application); - } - const lazyControllerHandler = new StimulusLazyControllerHandler(application, lazyControllers); - lazyControllerHandler.start(); +import { Application } from "@hotwired/stimulus"; +import { + eagerControllers, + isApplicationDebug, + lazyControllers +} from "./controllers.js"; +const controllerAttribute = "data-controller"; +const loadControllers = (application, eagerControllers2, lazyControllers2) => { + for (const name in eagerControllers2) { + registerController(name, eagerControllers2[name], application); + } + const lazyControllerHandler = new StimulusLazyControllerHandler( + application, + lazyControllers2 + ); + lazyControllerHandler.start(); }; const startStimulusApp = () => { - const application = Application.start(); - application.debug = isApplicationDebug; - loadControllers(application, eagerControllers, lazyControllers); - return application; + const application = Application.start(); + application.debug = isApplicationDebug; + loadControllers(application, eagerControllers, lazyControllers); + return application; }; class StimulusLazyControllerHandler { - constructor(application, lazyControllers) { - this.application = application; - this.lazyControllers = lazyControllers; + constructor(application, lazyControllers2) { + this.application = application; + this.lazyControllers = lazyControllers2; + } + start() { + this.lazyLoadExistingControllers(document.documentElement); + this.lazyLoadNewControllers(document.documentElement); + } + lazyLoadExistingControllers(element) { + Array.from(element.querySelectorAll(`[${controllerAttribute}]`)).flatMap(extractControllerNamesFrom).forEach((controllerName) => this.loadLazyController(controllerName)); + } + loadLazyController(name) { + if (!this.lazyControllers[name]) { + return; } - start() { - this.lazyLoadExistingControllers(document.documentElement); - this.lazyLoadNewControllers(document.documentElement); + const controllerLoader = this.lazyControllers[name]; + delete this.lazyControllers[name]; + if (!canRegisterController(name, this.application)) { + return; } - lazyLoadExistingControllers(element) { - Array.from(element.querySelectorAll(`[${controllerAttribute}]`)) - .flatMap(extractControllerNamesFrom) - .forEach((controllerName) => this.loadLazyController(controllerName)); + this.application.logDebugActivity(name, "lazy:loading"); + controllerLoader().then((controllerModule) => { + this.application.logDebugActivity(name, "lazy:loaded"); + registerController(name, controllerModule.default, this.application); + }).catch((error) => { + console.error(`Error loading controller "${name}":`, error); + }); + } + lazyLoadNewControllers(element) { + if (Object.keys(this.lazyControllers).length === 0) { + return; } - loadLazyController(name) { - if (!this.lazyControllers[name]) { - return; - } - const controllerLoader = this.lazyControllers[name]; - delete this.lazyControllers[name]; - if (!canRegisterController(name, this.application)) { - return; - } - this.application.logDebugActivity(name, 'lazy:loading'); - controllerLoader() - .then((controllerModule) => { - this.application.logDebugActivity(name, 'lazy:loaded'); - registerController(name, controllerModule.default, this.application); - }) - .catch((error) => { - console.error(`Error loading controller "${name}":`, error); - }); - } - lazyLoadNewControllers(element) { - if (Object.keys(this.lazyControllers).length === 0) { - return; - } - new MutationObserver((mutationsList) => { - for (const { attributeName, target, type } of mutationsList) { - switch (type) { - case 'attributes': { - if (attributeName === controllerAttribute && - target.getAttribute(controllerAttribute)) { - extractControllerNamesFrom(target).forEach((controllerName) => this.loadLazyController(controllerName)); - } - break; - } - case 'childList': { - this.lazyLoadExistingControllers(target); - } - } + new MutationObserver((mutationsList) => { + for (const { attributeName, target, type } of mutationsList) { + switch (type) { + case "attributes": { + if (attributeName === controllerAttribute && target.getAttribute(controllerAttribute)) { + extractControllerNamesFrom(target).forEach( + (controllerName) => this.loadLazyController(controllerName) + ); } - }).observe(element, { - attributeFilter: [controllerAttribute], - subtree: true, - childList: true, - }); - } + break; + } + case "childList": { + this.lazyLoadExistingControllers(target); + } + } + } + }).observe(element, { + attributeFilter: [controllerAttribute], + subtree: true, + childList: true + }); + } } function registerController(name, controller, application) { - if (canRegisterController(name, application)) { - application.register(name, controller); - } + if (canRegisterController(name, application)) { + application.register(name, controller); + } } function extractControllerNamesFrom(element) { - const controllerNameValue = element.getAttribute(controllerAttribute); - if (!controllerNameValue) { - return []; - } - return controllerNameValue.split(/\s+/).filter((content) => content.length); + const controllerNameValue = element.getAttribute(controllerAttribute); + if (!controllerNameValue) { + return []; + } + return controllerNameValue.split(/\s+/).filter((content) => content.length); } function canRegisterController(name, application) { - return !application.router.modulesByIdentifier.has(name); + return !application.router.modulesByIdentifier.has(name); } - -export { loadControllers, startStimulusApp }; +export { + loadControllers, + startStimulusApp +}; diff --git a/src/Svelte/assets/dist/components.d.ts b/src/Svelte/assets/dist/components.d.ts index 25fc4a359c9..8a33b2a735e 100644 --- a/src/Svelte/assets/dist/components.d.ts +++ b/src/Svelte/assets/dist/components.d.ts @@ -1,5 +1,8 @@ -import type { SvelteComponent } from 'svelte'; -export interface ComponentCollection { +import { SvelteComponent } from 'svelte'; + +interface ComponentCollection { [key: string]: SvelteComponent; } -export declare const components: ComponentCollection; +declare const components: ComponentCollection; + +export { type ComponentCollection, components }; diff --git a/src/Svelte/assets/dist/components.js b/src/Svelte/assets/dist/components.js index 2ea8342f45d..7973d4ddfa5 100644 --- a/src/Svelte/assets/dist/components.js +++ b/src/Svelte/assets/dist/components.js @@ -1,3 +1,4 @@ const components = {}; - -export { components }; +export { + components +}; diff --git a/src/Svelte/assets/dist/loader.d.ts b/src/Svelte/assets/dist/loader.d.ts index 747c71540d8..af59b59e176 100644 --- a/src/Svelte/assets/dist/loader.d.ts +++ b/src/Svelte/assets/dist/loader.d.ts @@ -1,9 +1,12 @@ -import type { SvelteComponent } from 'svelte'; -import { type ComponentCollection } from './components.js'; +import { SvelteComponent } from 'svelte'; +import { ComponentCollection } from './components.js'; + declare global { function resolveSvelteComponent(name: string): typeof SvelteComponent; interface Window { resolveSvelteComponent(name: string): typeof SvelteComponent; } } -export declare function registerSvelteControllerComponents(svelteComponents?: ComponentCollection): void; +declare function registerSvelteControllerComponents(svelteComponents?: ComponentCollection): void; + +export { registerSvelteControllerComponents }; diff --git a/src/Svelte/assets/dist/loader.js b/src/Svelte/assets/dist/loader.js index c73b3f25b14..cb4ef612c8c 100644 --- a/src/Svelte/assets/dist/loader.js +++ b/src/Svelte/assets/dist/loader.js @@ -1,14 +1,14 @@ -import { components } from './components.js'; - +import { components } from "./components.js"; function registerSvelteControllerComponents(svelteComponents = components) { - window.resolveSvelteComponent = (name) => { - const component = svelteComponents[name]; - if (typeof component === 'undefined') { - const possibleValues = Object.keys(svelteComponents).length > 0 ? Object.keys(svelteComponents).join(', ') : 'none'; - throw new Error(`Svelte controller "${name}" does not exist. Possible values: ${possibleValues}`); - } - return component; - }; + window.resolveSvelteComponent = (name) => { + const component = svelteComponents[name]; + if (typeof component === "undefined") { + const possibleValues = Object.keys(svelteComponents).length > 0 ? Object.keys(svelteComponents).join(", ") : "none"; + throw new Error(`Svelte controller "${name}" does not exist. Possible values: ${possibleValues}`); + } + return component; + }; } - -export { registerSvelteControllerComponents }; +export { + registerSvelteControllerComponents +}; diff --git a/src/Svelte/assets/dist/register_controller.d.ts b/src/Svelte/assets/dist/register_controller.d.ts index 6adf6b186d6..df9bb4a2db9 100644 --- a/src/Svelte/assets/dist/register_controller.d.ts +++ b/src/Svelte/assets/dist/register_controller.d.ts @@ -1,8 +1,11 @@ -import type { SvelteComponent } from 'svelte'; +import { SvelteComponent } from 'svelte'; + declare global { function resolveSvelteComponent(name: string): typeof SvelteComponent; interface Window { resolveSvelteComponent(name: string): typeof SvelteComponent; } } -export declare function registerSvelteControllerComponents(context: __WebpackModuleApi.RequireContext): void; +declare function registerSvelteControllerComponents(context: __WebpackModuleApi.RequireContext): void; + +export { registerSvelteControllerComponents }; diff --git a/src/Svelte/assets/dist/register_controller.js b/src/Svelte/assets/dist/register_controller.js index 23abca4bb8c..9f8c3d33613 100644 --- a/src/Svelte/assets/dist/register_controller.js +++ b/src/Svelte/assets/dist/register_controller.js @@ -1,18 +1,19 @@ function registerSvelteControllerComponents(context) { - const svelteControllers = {}; - const importAllSvelteComponents = (r) => { - r.keys().forEach((key) => { - svelteControllers[key] = r(key).default; - }); - }; - importAllSvelteComponents(context); - window.resolveSvelteComponent = (name) => { - const component = svelteControllers[`./${name}.svelte`]; - if (typeof component === 'undefined') { - throw new Error(`Svelte controller "${name}" does not exist`); - } - return component; - }; + const svelteControllers = {}; + const importAllSvelteComponents = (r) => { + r.keys().forEach((key) => { + svelteControllers[key] = r(key).default; + }); + }; + importAllSvelteComponents(context); + window.resolveSvelteComponent = (name) => { + const component = svelteControllers[`./${name}.svelte`]; + if (typeof component === "undefined") { + throw new Error(`Svelte controller "${name}" does not exist`); + } + return component; + }; } - -export { registerSvelteControllerComponents }; +export { + registerSvelteControllerComponents +}; diff --git a/src/Svelte/assets/dist/render_controller.d.ts b/src/Svelte/assets/dist/render_controller.d.ts index ae4130baf1a..6a768739515 100644 --- a/src/Svelte/assets/dist/render_controller.d.ts +++ b/src/Svelte/assets/dist/render_controller.d.ts @@ -1,6 +1,7 @@ import { Controller } from '@hotwired/stimulus'; -import type { SvelteComponent } from 'svelte'; -export default class extends Controller { private app; @@ -19,3 +20,5 @@ export default class extends Controller { - return allElements.indexOf(item) === index; - }); - const options = { - containers: containersList, - plugins: [ - 'slide' === this.themeValue - ? new SwupSlideTheme({ mainElement: mainElement }) - : new SwupFadeTheme({ mainElement: mainElement }), - new SwupFormsPlugin(), - ], - }; - if (this.hasMainElementValue) { - options.mainElement = this.mainElementValue; - } - if (this.hasAnimateHistoryBrowsingValue) { - options.animateHistoryBrowsing = this.animateHistoryBrowsingValue; - } - if (this.hasAnimationSelectorValue) { - options.animationSelector = this.animationSelectorValue; - } - if (this.hasCacheValue) { - options.cache = this.cacheValue; - } - if (this.hasLinkSelectorValue) { - options.linkSelector = this.linkSelectorValue; - } - if (this.debugValue) { - options.plugins.push(new SwupDebugPlugin()); - } - this.dispatchEvent('pre-connect', { options }); - const swup = new Swup(options); - this.dispatchEvent('connect', { swup, options }); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +import SwupDebugPlugin from "@swup/debug-plugin"; +import SwupFadeTheme from "@swup/fade-theme"; +import SwupFormsPlugin from "@swup/forms-plugin"; +import SwupSlideTheme from "@swup/slide-theme"; +import Swup from "swup"; +var controller_default = class extends Controller { + connect() { + const dataContainers = this.containersValue; + const mainElement = this.mainElementValue || dataContainers[0] || "#swup"; + const allElements = [mainElement].concat(dataContainers); + const containersList = allElements.filter((item, index) => { + return allElements.indexOf(item) === index; + }); + const options = { + containers: containersList, + plugins: [ + "slide" === this.themeValue ? new SwupSlideTheme({ mainElement }) : new SwupFadeTheme({ mainElement }), + new SwupFormsPlugin() + ] + }; + if (this.hasMainElementValue) { + options.mainElement = this.mainElementValue; } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'swup' }); + if (this.hasAnimateHistoryBrowsingValue) { + options.animateHistoryBrowsing = this.animateHistoryBrowsingValue; } -} -default_1.values = { - animateHistoryBrowsing: Boolean, - animationSelector: String, - cache: Boolean, - containers: Array, - linkSelector: String, - theme: String, - debug: Boolean, - mainElement: String, + if (this.hasAnimationSelectorValue) { + options.animationSelector = this.animationSelectorValue; + } + if (this.hasCacheValue) { + options.cache = this.cacheValue; + } + if (this.hasLinkSelectorValue) { + options.linkSelector = this.linkSelectorValue; + } + if (this.debugValue) { + options.plugins.push(new SwupDebugPlugin()); + } + this.dispatchEvent("pre-connect", { options }); + const swup = new Swup(options); + this.dispatchEvent("connect", { swup, options }); + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "swup" }); + } +}; +controller_default.values = { + animateHistoryBrowsing: Boolean, + animationSelector: String, + cache: Boolean, + containers: Array, + linkSelector: String, + // custom values + theme: String, + debug: Boolean, + mainElement: String +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/TogglePassword/assets/dist/controller.d.ts b/src/TogglePassword/assets/dist/controller.d.ts index 255887528fa..6d3c19074f4 100644 --- a/src/TogglePassword/assets/dist/controller.d.ts +++ b/src/TogglePassword/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { readonly visibleLabelValue: string; readonly visibleIconValue: string; readonly hiddenLabelValue: string; @@ -32,3 +33,5 @@ export default class extends Controller { toggle(event: any): void; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/TogglePassword/assets/dist/controller.js b/src/TogglePassword/assets/dist/controller.js index 0a266865f35..5189067f6aa 100644 --- a/src/TogglePassword/assets/dist/controller.js +++ b/src/TogglePassword/assets/dist/controller.js @@ -1,57 +1,62 @@ -import { Controller } from '@hotwired/stimulus'; - -class default_1 extends Controller { - constructor() { - super(...arguments); - this.isDisplayed = false; - this.visibleIcon = ` +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +var controller_default = class extends Controller { + constructor() { + super(...arguments); + this.isDisplayed = false; + this.visibleIcon = ` `; - this.hiddenIcon = ` + this.hiddenIcon = ` `; + } + connect() { + if (this.visibleIconValue !== "Default") { + this.visibleIcon = this.visibleIconValue; } - connect() { - if (this.visibleIconValue !== 'Default') { - this.visibleIcon = this.visibleIconValue; - } - if (this.hiddenIconValue !== 'Default') { - this.hiddenIcon = this.hiddenIconValue; - } - const button = this.createButton(); - this.element.insertAdjacentElement('afterend', button); - this.dispatchEvent('connect', { element: this.element, button: button }); + if (this.hiddenIconValue !== "Default") { + this.hiddenIcon = this.hiddenIconValue; } - createButton() { - const button = document.createElement('button'); - button.type = 'button'; - button.classList.add(...this.buttonClassesValue); - button.setAttribute('tabindex', '-1'); - button.addEventListener('click', this.toggle.bind(this)); - button.innerHTML = `${this.visibleIcon} ${this.visibleLabelValue}`; - return button; - } - toggle(event) { - this.isDisplayed = !this.isDisplayed; - const toggleButtonElement = event.currentTarget; - toggleButtonElement.innerHTML = this.isDisplayed - ? `${this.hiddenIcon} ${this.hiddenLabelValue}` - : `${this.visibleIcon} ${this.visibleLabelValue}`; - this.element.setAttribute('type', this.isDisplayed ? 'text' : 'password'); - this.dispatchEvent(this.isDisplayed ? 'show' : 'hide', { element: this.element, button: toggleButtonElement }); - } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'toggle-password' }); - } -} -default_1.values = { - visibleLabel: { type: String, default: 'Show' }, - visibleIcon: { type: String, default: 'Default' }, - hiddenLabel: { type: String, default: 'Hide' }, - hiddenIcon: { type: String, default: 'Default' }, - buttonClasses: Array, + const button = this.createButton(); + this.element.insertAdjacentElement("afterend", button); + this.dispatchEvent("connect", { element: this.element, button }); + } + /** + * @returns {HTMLButtonElement} + */ + createButton() { + const button = document.createElement("button"); + button.type = "button"; + button.classList.add(...this.buttonClassesValue); + button.setAttribute("tabindex", "-1"); + button.addEventListener("click", this.toggle.bind(this)); + button.innerHTML = `${this.visibleIcon} ${this.visibleLabelValue}`; + return button; + } + /** + * Toggle input type between "text" or "password" and update label accordingly + */ + toggle(event) { + this.isDisplayed = !this.isDisplayed; + const toggleButtonElement = event.currentTarget; + toggleButtonElement.innerHTML = this.isDisplayed ? `${this.hiddenIcon} ${this.hiddenLabelValue}` : `${this.visibleIcon} ${this.visibleLabelValue}`; + this.element.setAttribute("type", this.isDisplayed ? "text" : "password"); + this.dispatchEvent(this.isDisplayed ? "show" : "hide", { element: this.element, button: toggleButtonElement }); + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "toggle-password" }); + } +}; +controller_default.values = { + visibleLabel: { type: String, default: "Show" }, + visibleIcon: { type: String, default: "Default" }, + hiddenLabel: { type: String, default: "Hide" }, + hiddenIcon: { type: String, default: "Default" }, + buttonClasses: Array +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/Translator/assets/dist/formatters/formatter.d.ts b/src/Translator/assets/dist/formatters/formatter.d.ts deleted file mode 100644 index e55715f0960..00000000000 --- a/src/Translator/assets/dist/formatters/formatter.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function format(id: string, parameters: Record, locale: string): string; diff --git a/src/Translator/assets/dist/formatters/intl-formatter.d.ts b/src/Translator/assets/dist/formatters/intl-formatter.d.ts deleted file mode 100644 index 9892f4bd0d4..00000000000 --- a/src/Translator/assets/dist/formatters/intl-formatter.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function formatIntl(id: string, parameters: Record, locale: string): string; diff --git a/src/Translator/assets/dist/translator.d.ts b/src/Translator/assets/dist/translator.d.ts deleted file mode 100644 index 8d9f73e022c..00000000000 --- a/src/Translator/assets/dist/translator.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -export type DomainType = string; -export type LocaleType = string; -export type TranslationsType = Record; -export type NoParametersType = Record; -export type ParametersType = Record | NoParametersType; -export type RemoveIntlIcuSuffix = T extends `${infer U}+intl-icu` ? U : T; -export type DomainsOf = M extends Message ? keyof Translations : never; -export type LocaleOf = M extends Message ? Locale : never; -export type ParametersOf = M extends Message ? Translations[D] extends { - parameters: infer Parameters; -} ? Parameters : never : never; -export interface Message { - id: string; - translations: { - [domain in DomainType]: { - [locale in Locale]: string; - }; - }; -} -export declare function setLocale(locale: LocaleType | null): void; -export declare function getLocale(): LocaleType; -export declare function throwWhenNotFound(enabled: boolean): void; -export declare function setLocaleFallbacks(localeFallbacks: Record): void; -export declare function getLocaleFallbacks(): Record; -export declare function trans, D extends DomainsOf, P extends ParametersOf>(...args: P extends NoParametersType ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf]): string; diff --git a/src/Translator/assets/dist/translator_controller.d.ts b/src/Translator/assets/dist/translator_controller.d.ts index aff1f5870eb..e8d196c6838 100644 --- a/src/Translator/assets/dist/translator_controller.d.ts +++ b/src/Translator/assets/dist/translator_controller.d.ts @@ -1 +1,29 @@ -export * from './translator'; +type DomainType = string; +type LocaleType = string; +type TranslationsType = Record; +type NoParametersType = Record; +type ParametersType = Record | NoParametersType; +type RemoveIntlIcuSuffix = T extends `${infer U}+intl-icu` ? U : T; +type DomainsOf = M extends Message ? keyof Translations : never; +type LocaleOf = M extends Message ? Locale : never; +type ParametersOf = M extends Message ? Translations[D] extends { + parameters: infer Parameters; +} ? Parameters : never : never; +interface Message { + id: string; + translations: { + [domain in DomainType]: { + [locale in Locale]: string; + }; + }; +} +declare function setLocale(locale: LocaleType | null): void; +declare function getLocale(): LocaleType; +declare function throwWhenNotFound(enabled: boolean): void; +declare function setLocaleFallbacks(localeFallbacks: Record): void; +declare function getLocaleFallbacks(): Record; +declare function trans, D extends DomainsOf, P extends ParametersOf>(...args: P extends NoParametersType ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf]): string; + +export { type DomainType, type DomainsOf, type LocaleOf, type LocaleType, type Message, type NoParametersType, type ParametersOf, type ParametersType, type RemoveIntlIcuSuffix, type TranslationsType, getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans }; diff --git a/src/Translator/assets/dist/translator_controller.js b/src/Translator/assets/dist/translator_controller.js index c4504b75e3c..e5019f8c85c 100644 --- a/src/Translator/assets/dist/translator_controller.js +++ b/src/Translator/assets/dist/translator_controller.js @@ -1,283 +1,259 @@ -import { IntlMessageFormat } from 'intl-messageformat'; - +// src/utils.ts function strtr(string, replacePairs) { - const regex = Object.entries(replacePairs).map(([from]) => { - return from.replace(/([-[\]{}()*+?.\\^$|#,])/g, '\\$1'); - }); - if (regex.length === 0) { - return string; - } - return string.replace(new RegExp(regex.join('|'), 'g'), (matched) => replacePairs[matched].toString()); + const regex = Object.entries(replacePairs).map(([from]) => { + return from.replace(/([-[\]{}()*+?.\\^$|#,])/g, "\\$1"); + }); + if (regex.length === 0) { + return string; + } + return string.replace(new RegExp(regex.join("|"), "g"), (matched) => replacePairs[matched].toString()); } +// src/formatters/formatter.ts function format(id, parameters, locale) { - if (null === id || '' === id) { - return ''; - } - if (typeof parameters['%count%'] === 'undefined' || Number.isNaN(parameters['%count%'])) { - return strtr(id, parameters); - } - const number = Number(parameters['%count%']); - let parts = []; - if (/^\|+$/.test(id)) { - parts = id.split('|'); - } - else { - parts = id.match(/(?:\|\||[^|])+/g) || []; - } - const intervalRegex = /^(?({\s*(-?\d+(\.\d+)?[\s*,\s*\-?\d+(.\d+)?]*)\s*})|(?[[\]])\s*(?-Inf|-?\d+(\.\d+)?)\s*,\s*(?\+?Inf|-?\d+(\.\d+)?)\s*(?[[\]]))\s*(?.*?)$/s; - const standardRules = []; - for (let part of parts) { - part = part.trim().replace(/\|\|/g, '|'); - const matches = part.match(intervalRegex); - if (matches) { - const matchGroups = matches.groups || {}; - if (matches[2]) { - for (const n of matches[3].split(',')) { - if (number === Number(n)) { - return strtr(matchGroups.message, parameters); - } - } - } - else { - const leftNumber = '-Inf' === matchGroups.left ? Number.NEGATIVE_INFINITY : Number(matchGroups.left); - const rightNumber = ['Inf', '+Inf'].includes(matchGroups.right) - ? Number.POSITIVE_INFINITY - : Number(matchGroups.right); - if (('[' === matchGroups.left_delimiter ? number >= leftNumber : number > leftNumber) && - (']' === matchGroups.right_delimiter ? number <= rightNumber : number < rightNumber)) { - return strtr(matchGroups.message, parameters); - } - } + if (null === id || "" === id) { + return ""; + } + if (typeof parameters["%count%"] === "undefined" || Number.isNaN(parameters["%count%"])) { + return strtr(id, parameters); + } + const number = Number(parameters["%count%"]); + let parts = []; + if (/^\|+$/.test(id)) { + parts = id.split("|"); + } else { + parts = id.match(/(?:\|\||[^|])+/g) || []; + } + const intervalRegex = /^(?({\s*(-?\d+(\.\d+)?[\s*,\s*\-?\d+(.\d+)?]*)\s*})|(?[[\]])\s*(?-Inf|-?\d+(\.\d+)?)\s*,\s*(?\+?Inf|-?\d+(\.\d+)?)\s*(?[[\]]))\s*(?.*?)$/s; + const standardRules = []; + for (let part of parts) { + part = part.trim().replace(/\|\|/g, "|"); + const matches = part.match(intervalRegex); + if (matches) { + const matchGroups = matches.groups || {}; + if (matches[2]) { + for (const n of matches[3].split(",")) { + if (number === Number(n)) { + return strtr(matchGroups.message, parameters); + } } - else { - const ruleMatch = part.match(/^\w+:\s*(.*?)$/); - standardRules.push(ruleMatch ? ruleMatch[1] : part); + } else { + const leftNumber = "-Inf" === matchGroups.left ? Number.NEGATIVE_INFINITY : Number(matchGroups.left); + const rightNumber = ["Inf", "+Inf"].includes(matchGroups.right) ? Number.POSITIVE_INFINITY : Number(matchGroups.right); + if (("[" === matchGroups.left_delimiter ? number >= leftNumber : number > leftNumber) && ("]" === matchGroups.right_delimiter ? number <= rightNumber : number < rightNumber)) { + return strtr(matchGroups.message, parameters); } + } + } else { + const ruleMatch = part.match(/^\w+:\s*(.*?)$/); + standardRules.push(ruleMatch ? ruleMatch[1] : part); } - const position = getPluralizationRule(number, locale); - if (typeof standardRules[position] === 'undefined') { - if (1 === parts.length && typeof standardRules[0] !== 'undefined') { - return strtr(standardRules[0], parameters); - } - throw new Error(`Unable to choose a translation for "${id}" with locale "${locale}" for value "${number}". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %count% apples").`); + } + const position = getPluralizationRule(number, locale); + if (typeof standardRules[position] === "undefined") { + if (1 === parts.length && typeof standardRules[0] !== "undefined") { + return strtr(standardRules[0], parameters); } - return strtr(standardRules[position], parameters); + throw new Error( + `Unable to choose a translation for "${id}" with locale "${locale}" for value "${number}". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %count% apples").` + ); + } + return strtr(standardRules[position], parameters); } function getPluralizationRule(number, locale) { - number = Math.abs(number); - let _locale = locale; - if (locale === 'pt_BR' || locale === 'en_US_POSIX') { - return 0; - } - _locale = _locale.length > 3 ? _locale.substring(0, _locale.indexOf('_')) : _locale; - switch (_locale) { - case 'af': - case 'bn': - case 'bg': - case 'ca': - case 'da': - case 'de': - case 'el': - case 'en': - case 'en_US_POSIX': - case 'eo': - case 'es': - case 'et': - case 'eu': - case 'fa': - case 'fi': - case 'fo': - case 'fur': - case 'fy': - case 'gl': - case 'gu': - case 'ha': - case 'he': - case 'hu': - case 'is': - case 'it': - case 'ku': - case 'lb': - case 'ml': - case 'mn': - case 'mr': - case 'nah': - case 'nb': - case 'ne': - case 'nl': - case 'nn': - case 'no': - case 'oc': - case 'om': - case 'or': - case 'pa': - case 'pap': - case 'ps': - case 'pt': - case 'so': - case 'sq': - case 'sv': - case 'sw': - case 'ta': - case 'te': - case 'tk': - case 'ur': - case 'zu': - return 1 === number ? 0 : 1; - case 'am': - case 'bh': - case 'fil': - case 'fr': - case 'gun': - case 'hi': - case 'hy': - case 'ln': - case 'mg': - case 'nso': - case 'pt_BR': - case 'ti': - case 'wa': - return number < 2 ? 0 : 1; - case 'be': - case 'bs': - case 'hr': - case 'ru': - case 'sh': - case 'sr': - case 'uk': - return 1 === number % 10 && 11 !== number % 100 - ? 0 - : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) - ? 1 - : 2; - case 'cs': - case 'sk': - return 1 === number ? 0 : number >= 2 && number <= 4 ? 1 : 2; - case 'ga': - return 1 === number ? 0 : 2 === number ? 1 : 2; - case 'lt': - return 1 === number % 10 && 11 !== number % 100 - ? 0 - : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) - ? 1 - : 2; - case 'sl': - return 1 === number % 100 ? 0 : 2 === number % 100 ? 1 : 3 === number % 100 || 4 === number % 100 ? 2 : 3; - case 'mk': - return 1 === number % 10 ? 0 : 1; - case 'mt': - return 1 === number - ? 0 - : 0 === number || (number % 100 > 1 && number % 100 < 11) - ? 1 - : number % 100 > 10 && number % 100 < 20 - ? 2 - : 3; - case 'lv': - return 0 === number ? 0 : 1 === number % 10 && 11 !== number % 100 ? 1 : 2; - case 'pl': - return 1 === number - ? 0 - : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) - ? 1 - : 2; - case 'cy': - return 1 === number ? 0 : 2 === number ? 1 : 8 === number || 11 === number ? 2 : 3; - case 'ro': - return 1 === number ? 0 : 0 === number || (number % 100 > 0 && number % 100 < 20) ? 1 : 2; - case 'ar': - return 0 === number - ? 0 - : 1 === number - ? 1 - : 2 === number - ? 2 - : number % 100 >= 3 && number % 100 <= 10 - ? 3 - : number % 100 >= 11 && number % 100 <= 99 - ? 4 - : 5; - default: - return 0; - } + number = Math.abs(number); + let _locale2 = locale; + if (locale === "pt_BR" || locale === "en_US_POSIX") { + return 0; + } + _locale2 = _locale2.length > 3 ? _locale2.substring(0, _locale2.indexOf("_")) : _locale2; + switch (_locale2) { + case "af": + case "bn": + case "bg": + case "ca": + case "da": + case "de": + case "el": + case "en": + case "en_US_POSIX": + case "eo": + case "es": + case "et": + case "eu": + case "fa": + case "fi": + case "fo": + case "fur": + case "fy": + case "gl": + case "gu": + case "ha": + case "he": + case "hu": + case "is": + case "it": + case "ku": + case "lb": + case "ml": + case "mn": + case "mr": + case "nah": + case "nb": + case "ne": + case "nl": + case "nn": + case "no": + case "oc": + case "om": + case "or": + case "pa": + case "pap": + case "ps": + case "pt": + case "so": + case "sq": + case "sv": + case "sw": + case "ta": + case "te": + case "tk": + case "ur": + case "zu": + return 1 === number ? 0 : 1; + case "am": + case "bh": + case "fil": + case "fr": + case "gun": + case "hi": + case "hy": + case "ln": + case "mg": + case "nso": + case "pt_BR": + case "ti": + case "wa": + return number < 2 ? 0 : 1; + case "be": + case "bs": + case "hr": + case "ru": + case "sh": + case "sr": + case "uk": + return 1 === number % 10 && 11 !== number % 100 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2; + case "cs": + case "sk": + return 1 === number ? 0 : number >= 2 && number <= 4 ? 1 : 2; + case "ga": + return 1 === number ? 0 : 2 === number ? 1 : 2; + case "lt": + return 1 === number % 10 && 11 !== number % 100 ? 0 : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2; + case "sl": + return 1 === number % 100 ? 0 : 2 === number % 100 ? 1 : 3 === number % 100 || 4 === number % 100 ? 2 : 3; + case "mk": + return 1 === number % 10 ? 0 : 1; + case "mt": + return 1 === number ? 0 : 0 === number || number % 100 > 1 && number % 100 < 11 ? 1 : number % 100 > 10 && number % 100 < 20 ? 2 : 3; + case "lv": + return 0 === number ? 0 : 1 === number % 10 && 11 !== number % 100 ? 1 : 2; + case "pl": + return 1 === number ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) ? 1 : 2; + case "cy": + return 1 === number ? 0 : 2 === number ? 1 : 8 === number || 11 === number ? 2 : 3; + case "ro": + return 1 === number ? 0 : 0 === number || number % 100 > 0 && number % 100 < 20 ? 1 : 2; + case "ar": + return 0 === number ? 0 : 1 === number ? 1 : 2 === number ? 2 : number % 100 >= 3 && number % 100 <= 10 ? 3 : number % 100 >= 11 && number % 100 <= 99 ? 4 : 5; + default: + return 0; + } } +// src/formatters/intl-formatter.ts +import { IntlMessageFormat } from "intl-messageformat"; function formatIntl(id, parameters, locale) { - if (id === '') { - return ''; + if (id === "") { + return ""; + } + const intlMessage = new IntlMessageFormat(id, [locale.replace("_", "-")], void 0, { ignoreTag: true }); + parameters = { ...parameters }; + Object.entries(parameters).forEach(([key, value]) => { + if (key.includes("%") || key.includes("{")) { + delete parameters[key]; + parameters[key.replace(/[%{} ]/g, "").trim()] = value; } - const intlMessage = new IntlMessageFormat(id, [locale.replace('_', '-')], undefined, { ignoreTag: true }); - parameters = { ...parameters }; - Object.entries(parameters).forEach(([key, value]) => { - if (key.includes('%') || key.includes('{')) { - delete parameters[key]; - parameters[key.replace(/[%{} ]/g, '').trim()] = value; - } - }); - return intlMessage.format(parameters); + }); + return intlMessage.format(parameters); } -let _locale = null; -let _localeFallbacks = {}; -let _throwWhenNotFound = false; +// src/translator_controller.ts +var _locale = null; +var _localeFallbacks = {}; +var _throwWhenNotFound = false; function setLocale(locale) { - _locale = locale; + _locale = locale; } function getLocale() { - return (_locale || - document.documentElement.getAttribute('data-symfony-ux-translator-locale') || - (document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) || - 'en'); + return _locale || document.documentElement.getAttribute("data-symfony-ux-translator-locale") || // + (document.documentElement.lang ? document.documentElement.lang.replace("-", "_") : null) || // + "en"; } function throwWhenNotFound(enabled) { - _throwWhenNotFound = enabled; + _throwWhenNotFound = enabled; } function setLocaleFallbacks(localeFallbacks) { - _localeFallbacks = localeFallbacks; + _localeFallbacks = localeFallbacks; } function getLocaleFallbacks() { - return _localeFallbacks; + return _localeFallbacks; } -function trans(message, parameters = {}, domain = 'messages', locale = null) { - if (typeof domain === 'undefined') { - domain = 'messages'; +function trans(message, parameters = {}, domain = "messages", locale = null) { + if (typeof domain === "undefined") { + domain = "messages"; + } + if (typeof locale === "undefined" || null === locale) { + locale = getLocale(); + } + if (typeof message.translations === "undefined") { + return message.id; + } + const localesFallbacks = getLocaleFallbacks(); + const translationsIntl = message.translations[`${domain}+intl-icu`]; + if (typeof translationsIntl !== "undefined") { + while (typeof translationsIntl[locale] === "undefined") { + locale = localesFallbacks[locale]; + if (!locale) { + break; + } } - if (typeof locale === 'undefined' || null === locale) { - locale = getLocale(); + if (locale) { + return formatIntl(translationsIntl[locale], parameters, locale); } - if (typeof message.translations === 'undefined') { - return message.id; + } + const translations = message.translations[domain]; + if (typeof translations !== "undefined") { + while (typeof translations[locale] === "undefined") { + locale = localesFallbacks[locale]; + if (!locale) { + break; + } } - const localesFallbacks = getLocaleFallbacks(); - const translationsIntl = message.translations[`${domain}+intl-icu`]; - if (typeof translationsIntl !== 'undefined') { - while (typeof translationsIntl[locale] === 'undefined') { - locale = localesFallbacks[locale]; - if (!locale) { - break; - } - } - if (locale) { - return formatIntl(translationsIntl[locale], parameters, locale); - } + if (locale) { + return format(translations[locale], parameters, locale); } - const translations = message.translations[domain]; - if (typeof translations !== 'undefined') { - while (typeof translations[locale] === 'undefined') { - locale = localesFallbacks[locale]; - if (!locale) { - break; - } - } - if (locale) { - return format(translations[locale], parameters, locale); - } - } - if (_throwWhenNotFound) { - throw new Error(`No translation message found with id "${message.id}".`); - } - return message.id; + } + if (_throwWhenNotFound) { + throw new Error(`No translation message found with id "${message.id}".`); + } + return message.id; } - -export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans }; +export { + getLocale, + getLocaleFallbacks, + setLocale, + setLocaleFallbacks, + throwWhenNotFound, + trans +}; diff --git a/src/Translator/assets/dist/utils.d.ts b/src/Translator/assets/dist/utils.d.ts deleted file mode 100644 index fd3ca78fbeb..00000000000 --- a/src/Translator/assets/dist/utils.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function strtr(string: string, replacePairs: Record): string; diff --git a/src/Translator/assets/src/translator.ts b/src/Translator/assets/src/translator.ts deleted file mode 100644 index 5bddc02d4ff..00000000000 --- a/src/Translator/assets/src/translator.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -export type DomainType = string; -export type LocaleType = string; - -export type TranslationsType = Record; -export type NoParametersType = Record; -export type ParametersType = Record | NoParametersType; - -export type RemoveIntlIcuSuffix = T extends `${infer U}+intl-icu` ? U : T; -export type DomainsOf = M extends Message ? keyof Translations : never; -export type LocaleOf = M extends Message ? Locale : never; -export type ParametersOf = M extends Message - ? Translations[D] extends { parameters: infer Parameters } - ? Parameters - : never - : never; - -export interface Message { - id: string; - translations: { - [domain in DomainType]: { - [locale in Locale]: string; - }; - }; -} - -import { format } from './formatters/formatter'; -import { formatIntl } from './formatters/intl-formatter'; - -let _locale: LocaleType | null = null; -let _localeFallbacks: Record = {}; -let _throwWhenNotFound = false; - -export function setLocale(locale: LocaleType | null) { - _locale = locale; -} - -export function getLocale(): LocaleType { - return ( - _locale || - document.documentElement.getAttribute('data-symfony-ux-translator-locale') || // - (document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) || // - 'en' - ); -} - -export function throwWhenNotFound(enabled: boolean): void { - _throwWhenNotFound = enabled; -} - -export function setLocaleFallbacks(localeFallbacks: Record): void { - _localeFallbacks = localeFallbacks; -} - -export function getLocaleFallbacks(): Record { - return _localeFallbacks; -} - -/** - * Translates the given message, in ICU format (see https://formatjs.io/docs/intl-messageformat) or Symfony format (see below). - * - * When a number is provided as a parameter named "%count%", the message is parsed for plural - * forms and a translation is chosen according to this number using the following rules: - * - * Given a message with different plural translations separated by a - * pipe (|), this method returns the correct portion of the message based - * on the given number, locale and the pluralization rules in the message - * itself. - * - * The message supports two different types of pluralization rules: - * - * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples - * indexed: There is one apple|There are %count% apples - * - * The indexed solution can also contain labels (e.g. one: There is one apple). - * This is purely for making the translations more clear - it does not - * affect the functionality. - * - * The two methods can also be mixed: - * {0} There are no apples|one: There is one apple|more: There are %count% apples - * - * An interval can represent a finite set of numbers: - * {1,2,3,4} - * - * An interval can represent numbers between two numbers: - * [1, +Inf] - * ]-1,2[ - * - * The left delimiter can be [ (inclusive) or ] (exclusive). - * The right delimiter can be [ (exclusive) or ] (inclusive). - * Beside numbers, you can use -Inf and +Inf for the infinite. - * - * @see https://en.wikipedia.org/wiki/ISO_31-11 - * - * @param message The message - * @param parameters An array of parameters for the message - * @param domain The domain for the message or null to use the default - * @param locale The locale or null to use the default - */ -export function trans< - M extends Message, - D extends DomainsOf, - P extends ParametersOf, ->( - ...args: P extends NoParametersType - ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] - : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] -): string; -export function trans< - M extends Message, - D extends DomainsOf, - P extends ParametersOf, ->( - message: M, - parameters: P = {} as P, - domain: RemoveIntlIcuSuffix> | undefined = 'messages' as RemoveIntlIcuSuffix>, - locale: LocaleOf | null = null -): string { - if (typeof domain === 'undefined') { - domain = 'messages' as RemoveIntlIcuSuffix>; - } - - if (typeof locale === 'undefined' || null === locale) { - locale = getLocale() as LocaleOf; - } - - if (typeof message.translations === 'undefined') { - return message.id; - } - - const localesFallbacks = getLocaleFallbacks(); - - const translationsIntl = message.translations[`${domain}+intl-icu`]; - if (typeof translationsIntl !== 'undefined') { - while (typeof translationsIntl[locale] === 'undefined') { - locale = localesFallbacks[locale] as LocaleOf; - if (!locale) { - break; - } - } - - if (locale) { - return formatIntl(translationsIntl[locale], parameters, locale); - } - } - - const translations = message.translations[domain]; - if (typeof translations !== 'undefined') { - while (typeof translations[locale] === 'undefined') { - locale = localesFallbacks[locale] as LocaleOf; - if (!locale) { - break; - } - } - - if (locale) { - return format(translations[locale], parameters, locale); - } - } - - if (_throwWhenNotFound) { - throw new Error(`No translation message found with id "${message.id}".`); - } - - return message.id; -} diff --git a/src/Translator/assets/src/translator_controller.ts b/src/Translator/assets/src/translator_controller.ts index 7d2a0d93630..5bddc02d4ff 100644 --- a/src/Translator/assets/src/translator_controller.ts +++ b/src/Translator/assets/src/translator_controller.ts @@ -7,4 +7,168 @@ * file that was distributed with this source code. */ -export * from './translator'; +export type DomainType = string; +export type LocaleType = string; + +export type TranslationsType = Record; +export type NoParametersType = Record; +export type ParametersType = Record | NoParametersType; + +export type RemoveIntlIcuSuffix = T extends `${infer U}+intl-icu` ? U : T; +export type DomainsOf = M extends Message ? keyof Translations : never; +export type LocaleOf = M extends Message ? Locale : never; +export type ParametersOf = M extends Message + ? Translations[D] extends { parameters: infer Parameters } + ? Parameters + : never + : never; + +export interface Message { + id: string; + translations: { + [domain in DomainType]: { + [locale in Locale]: string; + }; + }; +} + +import { format } from './formatters/formatter'; +import { formatIntl } from './formatters/intl-formatter'; + +let _locale: LocaleType | null = null; +let _localeFallbacks: Record = {}; +let _throwWhenNotFound = false; + +export function setLocale(locale: LocaleType | null) { + _locale = locale; +} + +export function getLocale(): LocaleType { + return ( + _locale || + document.documentElement.getAttribute('data-symfony-ux-translator-locale') || // + (document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) || // + 'en' + ); +} + +export function throwWhenNotFound(enabled: boolean): void { + _throwWhenNotFound = enabled; +} + +export function setLocaleFallbacks(localeFallbacks: Record): void { + _localeFallbacks = localeFallbacks; +} + +export function getLocaleFallbacks(): Record { + return _localeFallbacks; +} + +/** + * Translates the given message, in ICU format (see https://formatjs.io/docs/intl-messageformat) or Symfony format (see below). + * + * When a number is provided as a parameter named "%count%", the message is parsed for plural + * forms and a translation is chosen according to this number using the following rules: + * + * Given a message with different plural translations separated by a + * pipe (|), this method returns the correct portion of the message based + * on the given number, locale and the pluralization rules in the message + * itself. + * + * The message supports two different types of pluralization rules: + * + * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples + * indexed: There is one apple|There are %count% apples + * + * The indexed solution can also contain labels (e.g. one: There is one apple). + * This is purely for making the translations more clear - it does not + * affect the functionality. + * + * The two methods can also be mixed: + * {0} There are no apples|one: There is one apple|more: There are %count% apples + * + * An interval can represent a finite set of numbers: + * {1,2,3,4} + * + * An interval can represent numbers between two numbers: + * [1, +Inf] + * ]-1,2[ + * + * The left delimiter can be [ (inclusive) or ] (exclusive). + * The right delimiter can be [ (exclusive) or ] (inclusive). + * Beside numbers, you can use -Inf and +Inf for the infinite. + * + * @see https://en.wikipedia.org/wiki/ISO_31-11 + * + * @param message The message + * @param parameters An array of parameters for the message + * @param domain The domain for the message or null to use the default + * @param locale The locale or null to use the default + */ +export function trans< + M extends Message, + D extends DomainsOf, + P extends ParametersOf, +>( + ...args: P extends NoParametersType + ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] + : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] +): string; +export function trans< + M extends Message, + D extends DomainsOf, + P extends ParametersOf, +>( + message: M, + parameters: P = {} as P, + domain: RemoveIntlIcuSuffix> | undefined = 'messages' as RemoveIntlIcuSuffix>, + locale: LocaleOf | null = null +): string { + if (typeof domain === 'undefined') { + domain = 'messages' as RemoveIntlIcuSuffix>; + } + + if (typeof locale === 'undefined' || null === locale) { + locale = getLocale() as LocaleOf; + } + + if (typeof message.translations === 'undefined') { + return message.id; + } + + const localesFallbacks = getLocaleFallbacks(); + + const translationsIntl = message.translations[`${domain}+intl-icu`]; + if (typeof translationsIntl !== 'undefined') { + while (typeof translationsIntl[locale] === 'undefined') { + locale = localesFallbacks[locale] as LocaleOf; + if (!locale) { + break; + } + } + + if (locale) { + return formatIntl(translationsIntl[locale], parameters, locale); + } + } + + const translations = message.translations[domain]; + if (typeof translations !== 'undefined') { + while (typeof translations[locale] === 'undefined') { + locale = localesFallbacks[locale] as LocaleOf; + if (!locale) { + break; + } + } + + if (locale) { + return format(translations[locale], parameters, locale); + } + } + + if (_throwWhenNotFound) { + throw new Error(`No translation message found with id "${message.id}".`); + } + + return message.id; +} diff --git a/src/Translator/assets/test/translator.test.ts b/src/Translator/assets/test/translator_controller.test.ts similarity index 99% rename from src/Translator/assets/test/translator.test.ts rename to src/Translator/assets/test/translator_controller.test.ts index af671d25923..38c6943fe02 100644 --- a/src/Translator/assets/test/translator.test.ts +++ b/src/Translator/assets/test/translator_controller.test.ts @@ -7,7 +7,7 @@ import { setLocaleFallbacks, throwWhenNotFound, trans, -} from '../src/translator'; +} from '../src/translator_controller'; describe('Translator', () => { beforeEach(() => { diff --git a/src/Turbo/assets/dist/turbo_controller.d.ts b/src/Turbo/assets/dist/turbo_controller.d.ts index ec02cc5aba6..610b6d54b5c 100644 --- a/src/Turbo/assets/dist/turbo_controller.d.ts +++ b/src/Turbo/assets/dist/turbo_controller.d.ts @@ -1,4 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -import '@hotwired/turbo'; -export default class extends Controller { + +declare class export_default extends Controller { } + +export { export_default as default }; diff --git a/src/Turbo/assets/dist/turbo_controller.js b/src/Turbo/assets/dist/turbo_controller.js index c9eaf80c6da..bcf6d05ba5a 100644 --- a/src/Turbo/assets/dist/turbo_controller.js +++ b/src/Turbo/assets/dist/turbo_controller.js @@ -1,7 +1,8 @@ -import { Controller } from '@hotwired/stimulus'; -import '@hotwired/turbo'; - -class turbo_controller extends Controller { -} - -export { turbo_controller as default }; +// src/turbo_controller.ts +import { Controller } from "@hotwired/stimulus"; +import "@hotwired/turbo"; +var turbo_controller_default = class extends Controller { +}; +export { + turbo_controller_default as default +}; diff --git a/src/Turbo/assets/dist/turbo_stream_controller.d.ts b/src/Turbo/assets/dist/turbo_stream_controller.d.ts index cc4db88a562..d373c3df089 100644 --- a/src/Turbo/assets/dist/turbo_stream_controller.d.ts +++ b/src/Turbo/assets/dist/turbo_stream_controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { static values: { topic: StringConstructor; topics: ArrayConstructor; @@ -19,3 +20,5 @@ export default class extends Controller { connect(): void; disconnect(): void; } + +export { export_default as default }; diff --git a/src/Turbo/assets/dist/turbo_stream_controller.js b/src/Turbo/assets/dist/turbo_stream_controller.js index d5962232feb..ecbbe412776 100644 --- a/src/Turbo/assets/dist/turbo_stream_controller.js +++ b/src/Turbo/assets/dist/turbo_stream_controller.js @@ -1,44 +1,42 @@ -import { Controller } from '@hotwired/stimulus'; -import { connectStreamSource, disconnectStreamSource } from '@hotwired/turbo'; - -class default_1 extends Controller { - initialize() { - const errorMessages = []; - if (!this.hasHubValue) - errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.'); - if (!this.hasTopicValue && !this.hasTopicsValue) - errorMessages.push('Either "topic" or "topics" value must be provided.'); - if (errorMessages.length) - throw new Error(errorMessages.join(' ')); - const u = new URL(this.hubValue); - if (this.hasTopicValue) { - u.searchParams.append('topic', this.topicValue); - } - else { - this.topicsValue.forEach((topic) => { - u.searchParams.append('topic', topic); - }); - } - this.url = u.toString(); +// src/turbo_stream_controller.ts +import { Controller } from "@hotwired/stimulus"; +import { connectStreamSource, disconnectStreamSource } from "@hotwired/turbo"; +var turbo_stream_controller_default = class extends Controller { + initialize() { + const errorMessages = []; + if (!this.hasHubValue) errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.'); + if (!this.hasTopicValue && !this.hasTopicsValue) + errorMessages.push('Either "topic" or "topics" value must be provided.'); + if (errorMessages.length) throw new Error(errorMessages.join(" ")); + const u = new URL(this.hubValue); + if (this.hasTopicValue) { + u.searchParams.append("topic", this.topicValue); + } else { + this.topicsValue.forEach((topic) => { + u.searchParams.append("topic", topic); + }); } - connect() { - if (this.url) { - this.es = new EventSource(this.url, { withCredentials: this.withCredentialsValue }); - connectStreamSource(this.es); - } + this.url = u.toString(); + } + connect() { + if (this.url) { + this.es = new EventSource(this.url, { withCredentials: this.withCredentialsValue }); + connectStreamSource(this.es); } - disconnect() { - if (this.es) { - this.es.close(); - disconnectStreamSource(this.es); - } + } + disconnect() { + if (this.es) { + this.es.close(); + disconnectStreamSource(this.es); } -} -default_1.values = { - topic: String, - topics: Array, - hub: String, - withCredentials: Boolean, + } +}; +turbo_stream_controller_default.values = { + topic: String, + topics: Array, + hub: String, + withCredentials: Boolean +}; +export { + turbo_stream_controller_default as default }; - -export { default_1 as default }; diff --git a/src/Typed/assets/dist/controller.d.ts b/src/Typed/assets/dist/controller.d.ts index a0edc995ab6..c356ddf4993 100644 --- a/src/Typed/assets/dist/controller.d.ts +++ b/src/Typed/assets/dist/controller.d.ts @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus'; -export default class extends Controller { + +declare class export_default extends Controller { static values: { strings: ArrayConstructor; typeSpeed: { @@ -71,3 +72,5 @@ export default class extends Controller { connect(): void; private dispatchEvent; } + +export { export_default as default }; diff --git a/src/Typed/assets/dist/controller.js b/src/Typed/assets/dist/controller.js index bb20688f045..242224a4b2f 100644 --- a/src/Typed/assets/dist/controller.js +++ b/src/Typed/assets/dist/controller.js @@ -1,55 +1,56 @@ -import { Controller } from '@hotwired/stimulus'; -import Typed from 'typed.js'; - -class default_1 extends Controller { - connect() { - const options = { - strings: this.stringsValue, - typeSpeed: this.typeSpeedValue, - smartBackspace: this.smartBackspaceValue, - startDelay: this.startDelayValue, - backSpeed: this.backSpeedValue, - shuffle: this.shuffleValue, - backDelay: this.backDelayValue, - fadeOut: this.fadeOutValue, - fadeOutClass: this.fadeOutClassValue, - fadeOutDelay: this.fadeOutDelayValue, - loop: this.loopValue, - loopCount: this.loopCountValue, - showCursor: this.showCursorValue, - cursorChar: this.cursorCharValue, - autoInsertCss: this.autoInsertCssValue, - attr: this.attrValue, - bindInputFocusEvents: this.bindInputFocusEventsValue, - contentType: this.contentTypeValue, - }; - this.dispatchEvent('pre-connect', { options }); - const typed = new Typed(this.element, options); - this.dispatchEvent('connect', { typed, options }); - } - dispatchEvent(name, payload) { - this.dispatch(name, { detail: payload, prefix: 'typed' }); - } -} -default_1.values = { - strings: Array, - typeSpeed: { type: Number, default: 30 }, - smartBackspace: { type: Boolean, default: true }, - startDelay: Number, - backSpeed: Number, - shuffle: Boolean, - backDelay: { type: Number, default: 700 }, - fadeOut: Boolean, - fadeOutClass: { type: String, default: 'typed-fade-out' }, - fadeOutDelay: { type: Number, default: 500 }, - loop: Boolean, - loopCount: { type: Number, default: Number.POSITIVE_INFINITY }, - showCursor: { type: Boolean, default: true }, - cursorChar: { type: String, default: '.' }, - autoInsertCss: { type: Boolean, default: true }, - attr: String, - bindInputFocusEvents: Boolean, - contentType: { type: String, default: 'html' }, +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +import Typed from "typed.js"; +var controller_default = class extends Controller { + connect() { + const options = { + strings: this.stringsValue, + typeSpeed: this.typeSpeedValue, + smartBackspace: this.smartBackspaceValue, + startDelay: this.startDelayValue, + backSpeed: this.backSpeedValue, + shuffle: this.shuffleValue, + backDelay: this.backDelayValue, + fadeOut: this.fadeOutValue, + fadeOutClass: this.fadeOutClassValue, + fadeOutDelay: this.fadeOutDelayValue, + loop: this.loopValue, + loopCount: this.loopCountValue, + showCursor: this.showCursorValue, + cursorChar: this.cursorCharValue, + autoInsertCss: this.autoInsertCssValue, + attr: this.attrValue, + bindInputFocusEvents: this.bindInputFocusEventsValue, + contentType: this.contentTypeValue + }; + this.dispatchEvent("pre-connect", { options }); + const typed = new Typed(this.element, options); + this.dispatchEvent("connect", { typed, options }); + } + dispatchEvent(name, payload) { + this.dispatch(name, { detail: payload, prefix: "typed" }); + } +}; +controller_default.values = { + strings: Array, + typeSpeed: { type: Number, default: 30 }, + smartBackspace: { type: Boolean, default: true }, + startDelay: Number, + backSpeed: Number, + shuffle: Boolean, + backDelay: { type: Number, default: 700 }, + fadeOut: Boolean, + fadeOutClass: { type: String, default: "typed-fade-out" }, + fadeOutDelay: { type: Number, default: 500 }, + loop: Boolean, + loopCount: { type: Number, default: Number.POSITIVE_INFINITY }, + showCursor: { type: Boolean, default: true }, + cursorChar: { type: String, default: "." }, + autoInsertCss: { type: Boolean, default: true }, + attr: String, + bindInputFocusEvents: Boolean, + contentType: { type: String, default: "html" } +}; +export { + controller_default as default }; - -export { default_1 as default }; diff --git a/src/Vue/assets/dist/components.d.ts b/src/Vue/assets/dist/components.d.ts index 146332315c9..f345d1527b8 100644 --- a/src/Vue/assets/dist/components.d.ts +++ b/src/Vue/assets/dist/components.d.ts @@ -1,5 +1,8 @@ -import type { Component } from 'vue'; -export interface ComponentCollection { +import { Component } from 'vue'; + +interface ComponentCollection { [key: string]: Component; } -export declare const components: ComponentCollection; +declare const components: ComponentCollection; + +export { type ComponentCollection, components }; diff --git a/src/Vue/assets/dist/components.js b/src/Vue/assets/dist/components.js index 2ea8342f45d..7973d4ddfa5 100644 --- a/src/Vue/assets/dist/components.js +++ b/src/Vue/assets/dist/components.js @@ -1,3 +1,4 @@ const components = {}; - -export { components }; +export { + components +}; diff --git a/src/Vue/assets/dist/loader.d.ts b/src/Vue/assets/dist/loader.d.ts index 7af789f0487..67d643d0a4d 100644 --- a/src/Vue/assets/dist/loader.d.ts +++ b/src/Vue/assets/dist/loader.d.ts @@ -1,9 +1,12 @@ -import type { Component } from 'vue'; -import { type ComponentCollection } from './components.js'; +import { Component } from 'vue'; +import { ComponentCollection } from './components.js'; + declare global { function resolveVueComponent(name: string): Component; interface Window { resolveVueComponent(name: string): Component; } } -export declare function registerVueControllerComponents(vueControllers?: ComponentCollection): void; +declare function registerVueControllerComponents(vueControllers?: ComponentCollection): void; + +export { registerVueControllerComponents }; diff --git a/src/Vue/assets/dist/loader.js b/src/Vue/assets/dist/loader.js index 4cef27dfbd1..9e11ba9eed2 100644 --- a/src/Vue/assets/dist/loader.js +++ b/src/Vue/assets/dist/loader.js @@ -1,17 +1,17 @@ -import { components } from './components.js'; - +import { components } from "./components.js"; function registerVueControllerComponents(vueControllers = components) { - function loadComponent(name) { - const component = vueControllers[name]; - if (typeof component === 'undefined') { - const possibleValues = Object.keys(vueControllers).length > 0 ? Object.keys(vueControllers).join(', ') : 'none'; - throw new Error(`Vue controller "${name}" does not exist. Possible values: ${possibleValues}`); - } - return component; + function loadComponent(name) { + const component = vueControllers[name]; + if (typeof component === "undefined") { + const possibleValues = Object.keys(vueControllers).length > 0 ? Object.keys(vueControllers).join(", ") : "none"; + throw new Error(`Vue controller "${name}" does not exist. Possible values: ${possibleValues}`); } - window.resolveVueComponent = (name) => { - return loadComponent(name); - }; + return component; + } + window.resolveVueComponent = (name) => { + return loadComponent(name); + }; } - -export { registerVueControllerComponents }; +export { + registerVueControllerComponents +}; diff --git a/src/Vue/assets/dist/register_controller.d.ts b/src/Vue/assets/dist/register_controller.d.ts index 87348f24cd1..695915a6568 100644 --- a/src/Vue/assets/dist/register_controller.d.ts +++ b/src/Vue/assets/dist/register_controller.d.ts @@ -1,8 +1,11 @@ -import type { Component } from 'vue'; +import { Component } from 'vue'; + declare global { function resolveVueComponent(name: string): Component; interface Window { resolveVueComponent(name: string): Component; } } -export declare function registerVueControllerComponents(context: __WebpackModuleApi.RequireContext): void; +declare function registerVueControllerComponents(context: __WebpackModuleApi.RequireContext): void; + +export { registerVueControllerComponents }; diff --git a/src/Vue/assets/dist/register_controller.js b/src/Vue/assets/dist/register_controller.js index 22b95f6b531..aad72d7845d 100644 --- a/src/Vue/assets/dist/register_controller.js +++ b/src/Vue/assets/dist/register_controller.js @@ -1,44 +1,46 @@ -import { defineAsyncComponent } from 'vue'; - +import { defineAsyncComponent } from "vue"; function registerVueControllerComponents(context) { - const vueControllers = context.keys().reduce((acc, key) => { - acc[key] = undefined; - return acc; - }, {}); - function loadComponent(name) { - const componentPath = `./${name}.vue`; - if (!(componentPath in vueControllers)) { - const possibleValues = Object.keys(vueControllers).map((key) => key.replace('./', '').replace('.vue', '')); - throw new Error(`Vue controller "${name}" does not exist. Possible values: ${possibleValues.join(', ')}`); - } - if (typeof vueControllers[componentPath] === 'undefined') { - const module = context(componentPath); - if (module.default) { - vueControllers[componentPath] = module.default; - } - else if (module instanceof Promise) { - vueControllers[componentPath] = defineAsyncComponent(() => new Promise((resolve, reject) => { - module - .then((resolvedModule) => { - if (resolvedModule.default) { - resolve(resolvedModule.default); - } - else { - reject(new Error(`Cannot find default export in async Vue controller "${name}".`)); - } - }) - .catch(reject); - })); - } - else { - throw new Error(`Vue controller "${name}" does not exist.`); - } - } - return vueControllers[componentPath]; + const vueControllers = context.keys().reduce( + (acc, key) => { + acc[key] = void 0; + return acc; + }, + {} + ); + function loadComponent(name) { + const componentPath = `./${name}.vue`; + if (!(componentPath in vueControllers)) { + const possibleValues = Object.keys(vueControllers).map((key) => key.replace("./", "").replace(".vue", "")); + throw new Error(`Vue controller "${name}" does not exist. Possible values: ${possibleValues.join(", ")}`); } - window.resolveVueComponent = (name) => { - return loadComponent(name); - }; + if (typeof vueControllers[componentPath] === "undefined") { + const module = context(componentPath); + if (module.default) { + vueControllers[componentPath] = module.default; + } else if (module instanceof Promise) { + vueControllers[componentPath] = defineAsyncComponent( + () => new Promise((resolve, reject) => { + module.then((resolvedModule) => { + if (resolvedModule.default) { + resolve(resolvedModule.default); + } else { + reject( + new Error(`Cannot find default export in async Vue controller "${name}".`) + ); + } + }).catch(reject); + }) + ); + } else { + throw new Error(`Vue controller "${name}" does not exist.`); + } + } + return vueControllers[componentPath]; + } + window.resolveVueComponent = (name) => { + return loadComponent(name); + }; } - -export { registerVueControllerComponents }; +export { + registerVueControllerComponents +}; diff --git a/src/Vue/assets/dist/render_controller.d.ts b/src/Vue/assets/dist/render_controller.d.ts index 205d99e8377..dd564df9428 100644 --- a/src/Vue/assets/dist/render_controller.d.ts +++ b/src/Vue/assets/dist/render_controller.d.ts @@ -1,6 +1,7 @@ import { Controller } from '@hotwired/stimulus'; -import { type App } from 'vue'; -export default class extends Controller; }> { private props; @@ -15,3 +16,5 @@ export default class extends Controller