1 line
No EOL
58 KiB
JSON
1 line
No EOL
58 KiB
JSON
{"hash":"aebc4a389b667764","sourceSize":40082,"sourceSizeGzipped":14925,"compileTime":"33.525268ms","name":"datastar-1-0-0-beta-7-aebc4a389b667764","downloadURL":"/bundler/download/datastar-1-0-0-beta-7-aebc4a389b667764.zip","manifest":{"version":"1.0.0-beta.7","plugins":[{"label":"DELETE","name":"DELETE","path":"../plugins/official/backend/actions/delete","type":"","author":"","slug":"Use a DELETE request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"material-symbols:delete-outline","key":"static_library_source_plugins_official_backend_actions_delete_ts","contents":"// Icon: material-symbols:delete-outline\n// Slug: Use a DELETE request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n type ActionPlugin,\n PluginType,\n type RuntimeContext,\n} from '../../../../engine/types'\nimport { type SSEArgs, sse } from './sse'\n\nexport const DELETE: ActionPlugin = {\n type: PluginType.Action,\n name: 'delete',\n fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) =\u003e {\n return sse(ctx, 'DELETE', url, { ...args })\n },\n}\n"},{"label":"GET","name":"GET","path":"../plugins/official/backend/actions/get","type":"","author":"","slug":"Use a GET request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"ic:baseline-get-app","key":"static_library_source_plugins_official_backend_actions_get_ts","contents":"// Icon: ic:baseline-get-app\n// Slug: Use a GET request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n type ActionPlugin,\n PluginType,\n type RuntimeContext,\n} from '../../../../engine/types'\nimport { type SSEArgs, sse } from './sse'\n\nexport const GET: ActionPlugin = {\n type: PluginType.Action,\n name: 'get',\n fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) =\u003e {\n return sse(ctx, 'GET', url, { ...args })\n },\n}\n"},{"label":"PATCH","name":"PATCH","path":"../plugins/official/backend/actions/patch","type":"","author":"","slug":"Use a PATCH request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"fluent:patch-24-filled","key":"static_library_source_plugins_official_backend_actions_patch_ts","contents":"// Icon: fluent:patch-24-filled\n// Slug: Use a PATCH request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n type ActionPlugin,\n PluginType,\n type RuntimeContext,\n} from '../../../../engine/types'\nimport { type SSEArgs, sse } from './sse'\n\nexport const PATCH: ActionPlugin = {\n type: PluginType.Action,\n name: 'patch',\n fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) =\u003e {\n return sse(ctx, 'PATCH', url, { ...args })\n },\n}\n"},{"label":"POST","name":"POST","path":"../plugins/official/backend/actions/post","type":"","author":"","slug":"Use a POST request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"ri:signpost-fill","key":"static_library_source_plugins_official_backend_actions_post_ts","contents":"// Icon: ri:signpost-fill\n// Slug: Use a POST request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n type ActionPlugin,\n PluginType,\n type RuntimeContext,\n} from '../../../../engine/types'\nimport { type SSEArgs, sse } from './sse'\n\nexport const POST: ActionPlugin = {\n type: PluginType.Action,\n name: 'post',\n fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) =\u003e {\n return sse(ctx, 'POST', url, { ...args })\n },\n}\n"},{"label":"PUT","name":"PUT","path":"../plugins/official/backend/actions/put","type":"","author":"","slug":"Use a PUT request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"material-symbols:arrows-input","key":"static_library_source_plugins_official_backend_actions_put_ts","contents":"// Icon: material-symbols:arrows-input\n// Slug: Use a PUT request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n type ActionPlugin,\n PluginType,\n type RuntimeContext,\n} from '../../../../engine/types'\nimport { type SSEArgs, sse } from './sse'\n\nexport const PUT: ActionPlugin = {\n type: PluginType.Action,\n name: 'put',\n fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) =\u003e {\n return sse(ctx, 'PUT', url, { ...args })\n },\n}\n"},{"label":"Indicator","name":"Indicator","path":"../plugins/official/backend/attributes/indicator","type":"","author":"","slug":"Sets the indicator signal used when fetching data via SSE","description":"must be a valid signal name","icon":"material-symbols:network-wifi","key":"static_library_source_plugins_official_backend_attributes_indicator_ts","contents":"// Icon: material-symbols:network-wifi\n// Slug: Sets the indicator signal used when fetching data via SSE\n// Description: must be a valid signal name\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { modifyCasing, trimDollarSignPrefix } from '../../../../utils/text'\nimport {\n DATASTAR_SSE_EVENT,\n type DatastarSSEEvent,\n FINISHED,\n STARTED,\n} from '../shared'\n\nexport const Indicator: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'indicator',\n keyReq: Requirement.Exclusive,\n valReq: Requirement.Exclusive,\n onLoad: ({ el, key, mods, signals, value }) =\u003e {\n const signalName = key ? modifyCasing(key, mods) : trimDollarSignPrefix(value)\n const signal = signals.upsertIfMissing(signalName, false)\n const watcher = (event: CustomEvent\u003cDatastarSSEEvent\u003e) =\u003e {\n const {\n type,\n argsRaw: { elId },\n } = event.detail\n if (elId !== el.id) return\n switch (type) {\n case STARTED:\n signal.value = true\n break\n case FINISHED:\n signal.value = false\n break\n }\n }\n document.addEventListener(DATASTAR_SSE_EVENT, watcher)\n\n return () =\u003e {\n document.removeEventListener(DATASTAR_SSE_EVENT, watcher)\n }\n },\n}\n"},{"label":"ExecuteScript","name":"ExecuteScript","path":"../plugins/official/backend/watchers/executeScript","type":"","author":"","slug":"Execute JavaScript using a Server-Sent Event","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"tabler:file-type-js","key":"static_library_source_plugins_official_backend_watchers_execute_script_ts","contents":"// Icon: tabler:file-type-js\n// Slug: Execute JavaScript using a Server-Sent Event\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n DefaultExecuteScriptAttributes,\n DefaultExecuteScriptAutoRemove,\n EventTypes,\n} from '../../../../engine/consts'\nimport { initErr } from '../../../../engine/errors'\nimport { PluginType, type WatcherPlugin } from '../../../../engine/types'\nimport { isBoolString } from '../../../../utils/text'\nimport { datastarSSEEventWatcher } from '../shared'\n\nexport const ExecuteScript: WatcherPlugin = {\n type: PluginType.Watcher,\n name: EventTypes.ExecuteScript,\n onGlobalInit: async (ctx) =\u003e {\n datastarSSEEventWatcher(\n EventTypes.ExecuteScript,\n ({\n autoRemove: autoRemoveRaw = `${DefaultExecuteScriptAutoRemove}`,\n attributes: attributesRaw = DefaultExecuteScriptAttributes,\n script,\n }) =\u003e {\n const autoRemove = isBoolString(autoRemoveRaw)\n if (!script?.length) {\n throw initErr('NoScriptProvided', ctx)\n }\n const scriptEl = document.createElement('script')\n for (const attr of attributesRaw.split('\\n')) {\n const pivot = attr.indexOf(' ')\n const key = pivot ? attr.slice(0, pivot) : attr\n const value = pivot ? attr.slice(pivot) : ''\n scriptEl.setAttribute(key.trim(), value.trim())\n }\n scriptEl.text = script\n document.head.appendChild(scriptEl)\n if (autoRemove) {\n scriptEl.remove()\n }\n },\n )\n },\n}\n"},{"label":"MergeFragments","name":"MergeFragments","path":"../plugins/official/backend/watchers/mergeFragments","type":"","author":"","slug":"Merge fragments into the DOM using a Server-Sent Event","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"material-symbols:cloud-download","key":"static_library_source_plugins_official_backend_watchers_merge_fragments_ts","contents":"// Icon: material-symbols:cloud-download\n// Slug: Merge fragments into the DOM using a Server-Sent Event\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n DefaultFragmentMergeMode,\n DefaultFragmentsSettleDurationMs,\n DefaultFragmentsUseViewTransitions,\n EventTypes,\n FragmentMergeModes,\n} from '../../../../engine/consts'\nimport { initErr } from '../../../../engine/errors'\nimport {\n type HTMLorSVGElement,\n type InitContext,\n PluginType,\n type WatcherPlugin,\n} from '../../../../engine/types'\nimport { elUniqId, walkDOM } from '../../../../utils/dom'\nimport { camel, isBoolString } from '../../../../utils/text'\nimport {\n docWithViewTransitionAPI,\n supportsViewTransitions,\n} from '../../../../utils/view-transtions'\nimport { Idiomorph } from '../../../../vendored/idiomorph.esm'\nimport {\n SETTLING_CLASS,\n SWAPPING_CLASS,\n datastarSSEEventWatcher,\n} from '../shared'\n\nexport const MergeFragments: WatcherPlugin = {\n type: PluginType.Watcher,\n name: EventTypes.MergeFragments,\n onGlobalInit: async (ctx) =\u003e {\n const fragmentContainer = document.createElement('template')\n datastarSSEEventWatcher(\n EventTypes.MergeFragments,\n ({\n fragments: fragmentsRaw = '\u003cdiv\u003e\u003c/div\u003e',\n selector = '',\n mergeMode = DefaultFragmentMergeMode,\n settleDuration:\n settleDurationRaw = `${DefaultFragmentsSettleDurationMs}`,\n useViewTransition:\n useViewTransitionRaw = `${DefaultFragmentsUseViewTransitions}`,\n }) =\u003e {\n const settleDuration = Number.parseInt(settleDurationRaw)\n const useViewTransition = isBoolString(useViewTransitionRaw)\n\n fragmentContainer.innerHTML = fragmentsRaw.trim()\n const fragments = [...fragmentContainer.content.children]\n for (const fragment of fragments) {\n if (!(fragment instanceof Element)) {\n throw initErr('NoFragmentsFound', ctx)\n }\n\n const selectorOrID = selector || `#${fragment.getAttribute('id')}`\n const targets = [...(document.querySelectorAll(selectorOrID) || [])]\n if (!targets.length) {\n throw initErr('NoTargetsFound', ctx, { selectorOrID })\n }\n\n if (useViewTransition \u0026\u0026 supportsViewTransitions) {\n docWithViewTransitionAPI.startViewTransition(() =\u003e\n applyToTargets(ctx, mergeMode, settleDuration, fragment, targets),\n )\n } else {\n applyToTargets(ctx, mergeMode, settleDuration, fragment, targets)\n }\n }\n },\n )\n },\n}\n\nfunction applyToTargets(\n ctx: InitContext,\n mergeMode: string,\n settleDuration: number,\n fragment: Element,\n capturedTargets: Element[],\n) {\n for (const initialTarget of capturedTargets) {\n initialTarget.classList.add(SWAPPING_CLASS)\n const originalHTML = initialTarget.outerHTML\n let modifiedTarget = initialTarget\n switch (mergeMode) {\n case FragmentMergeModes.Morph: {\n const toApply = new Map\u003cElement, Array\u003cstring\u003e\u003e()\n\n const fragmentWithIDs = fragment.cloneNode(true) as HTMLorSVGElement\n walkDOM(fragmentWithIDs, (el) =\u003e {\n if (!el.id?.length \u0026\u0026 Object.keys(el.dataset).length) {\n el.id = elUniqId(el)\n }\n })\n\n const result = Idiomorph.morph(modifiedTarget, fragmentWithIDs, {\n restoreFocus: true,\n callbacks: {\n beforeAttributeUpdated: (\n argument: string,\n el: Element,\n mode: 'update' | 'remove',\n ): boolean =\u003e {\n if (mode === 'update' \u0026\u0026 argument.startsWith('data-')) {\n let elAddAttrs = toApply.get(el)\n if (!elAddAttrs) {\n elAddAttrs = []\n toApply.set(el, elAddAttrs)\n }\n const name = argument.slice('data-'.length)\n elAddAttrs.push(camel(name))\n }\n return true\n },\n },\n })\n if (result?.length) {\n modifiedTarget = result[0] as Element\n for (const [el, attrs] of toApply.entries()) {\n for (const attr of attrs) {\n ctx.applyAttributePlugin(el as HTMLorSVGElement, attr)\n }\n }\n }\n\n break\n }\n case FragmentMergeModes.Inner:\n // Replace the contents of the target element with the outer HTML of the response\n modifiedTarget.innerHTML = fragment.outerHTML\n break\n case FragmentMergeModes.Outer:\n // Replace the entire target element with the response\n modifiedTarget.replaceWith(fragment)\n break\n case FragmentMergeModes.Prepend:\n // Insert the response before the first child of the target element\n modifiedTarget.prepend(fragment)\n break\n case FragmentMergeModes.Append:\n // Insert the response after the last child of the target element\n modifiedTarget.append(fragment)\n break\n case FragmentMergeModes.Before:\n // Insert the response before the target element\n modifiedTarget.before(fragment)\n break\n case FragmentMergeModes.After:\n // Insert the response after the target element\n modifiedTarget.after(fragment)\n break\n case FragmentMergeModes.UpsertAttributes:\n // Upsert the attributes of the target element\n for (const attrName of fragment.getAttributeNames()) {\n const value = fragment.getAttribute(attrName)!\n modifiedTarget.setAttribute(attrName, value)\n }\n break\n default:\n throw initErr('InvalidMergeMode', ctx, { mergeMode })\n }\n\n const cl = modifiedTarget.classList\n cl?.add(SWAPPING_CLASS)\n\n setTimeout(() =\u003e {\n initialTarget.classList.remove(SWAPPING_CLASS)\n cl?.remove(SWAPPING_CLASS)\n }, settleDuration)\n\n const revisedHTML = modifiedTarget.outerHTML\n\n if (cl \u0026\u0026 originalHTML !== revisedHTML) {\n cl.add(SETTLING_CLASS)\n setTimeout(() =\u003e {\n cl.remove(SETTLING_CLASS)\n }, settleDuration)\n }\n }\n}\n"},{"label":"MergeSignals","name":"MergeSignals","path":"../plugins/official/backend/watchers/mergeSignals","type":"","author":"","slug":"Merge signals using a Server-Sent Event","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"material-symbols:settings-input-antenna","key":"static_library_source_plugins_official_backend_watchers_merge_signals_ts","contents":"// Icon: material-symbols:settings-input-antenna\n// Slug: Merge signals using a Server-Sent Event\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n DefaultMergeSignalsOnlyIfMissing,\n EventTypes,\n} from '../../../../engine/consts'\nimport { PluginType, type WatcherPlugin } from '../../../../engine/types'\nimport { isBoolString, jsStrToObject } from '../../../../utils/text'\nimport { datastarSSEEventWatcher } from '../shared'\n\nexport const MergeSignals: WatcherPlugin = {\n type: PluginType.Watcher,\n name: EventTypes.MergeSignals,\n onGlobalInit: async (ctx) =\u003e {\n datastarSSEEventWatcher(\n EventTypes.MergeSignals,\n ({\n signals: raw = '{}',\n onlyIfMissing: onlyIfMissingRaw = `${DefaultMergeSignalsOnlyIfMissing}`,\n }) =\u003e {\n const { signals } = ctx\n const onlyIfMissing = isBoolString(onlyIfMissingRaw)\n signals.merge(jsStrToObject(raw), onlyIfMissing)\n },\n )\n },\n}\n"},{"label":"RemoveFragments","name":"RemoveFragments","path":"../plugins/official/backend/watchers/removeFragments","type":"","author":"","slug":"Remove fragments from the DOM using a Server-Sent Event","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"material-symbols:settings-input-antenna","key":"static_library_source_plugins_official_backend_watchers_remove_fragments_ts","contents":"// Icon: material-symbols:settings-input-antenna\n// Slug: Remove fragments from the DOM using a Server-Sent Event\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport {\n DefaultFragmentsSettleDurationMs,\n DefaultFragmentsUseViewTransitions,\n EventTypes,\n} from '../../../../engine/consts'\nimport { initErr } from '../../../../engine/errors'\nimport { PluginType, type WatcherPlugin } from '../../../../engine/types'\nimport { isBoolString } from '../../../../utils/text'\nimport {\n docWithViewTransitionAPI,\n supportsViewTransitions,\n} from '../../../../utils/view-transtions'\nimport { SWAPPING_CLASS, datastarSSEEventWatcher } from '../shared'\n\nexport const RemoveFragments: WatcherPlugin = {\n type: PluginType.Watcher,\n name: EventTypes.RemoveFragments,\n onGlobalInit: async (ctx) =\u003e {\n datastarSSEEventWatcher(\n EventTypes.RemoveFragments,\n ({\n selector,\n settleDuration:\n settleDurationRaw = `${DefaultFragmentsSettleDurationMs}`,\n useViewTransition:\n useViewTransitionRaw = `${DefaultFragmentsUseViewTransitions}`,\n }) =\u003e {\n if (!selector.length) {\n throw initErr('NoSelectorProvided', ctx)\n }\n\n const settleDuration = Number.parseInt(settleDurationRaw)\n const useViewTransition = isBoolString(useViewTransitionRaw)\n const removeTargets = document.querySelectorAll(selector)\n\n const applyToTargets = () =\u003e {\n for (const target of removeTargets) {\n target.classList.add(SWAPPING_CLASS)\n }\n\n setTimeout(() =\u003e {\n for (const target of removeTargets) {\n target.remove()\n }\n }, settleDuration)\n }\n\n if (useViewTransition \u0026\u0026 supportsViewTransitions) {\n docWithViewTransitionAPI.startViewTransition(() =\u003e applyToTargets())\n } else {\n applyToTargets()\n }\n },\n )\n },\n}\n"},{"label":"RemoveSignals","name":"RemoveSignals","path":"../plugins/official/backend/watchers/removeSignals","type":"","author":"","slug":"Remove signals using a Server-Sent Event","description":"Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.","icon":"material-symbols:settings-input-antenna","key":"static_library_source_plugins_official_backend_watchers_remove_signals_ts","contents":"// Icon: material-symbols:settings-input-antenna\n// Slug: Remove signals using a Server-Sent Event\n// Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client.\n\nimport { EventTypes } from '../../../../engine/consts'\nimport { initErr } from '../../../../engine/errors'\nimport { PluginType, type WatcherPlugin } from '../../../../engine/types'\nimport { datastarSSEEventWatcher } from '../shared'\n\nexport const RemoveSignals: WatcherPlugin = {\n type: PluginType.Watcher,\n name: EventTypes.RemoveSignals,\n onGlobalInit: async (ctx) =\u003e {\n datastarSSEEventWatcher(\n EventTypes.RemoveSignals,\n ({ paths: pathsRaw = '' }) =\u003e {\n const paths = pathsRaw.split('\\n').map((p) =\u003e p.trim())\n if (!paths?.length) {\n throw initErr('NoPathsProvided', ctx)\n }\n ctx.signals.remove(...paths)\n },\n )\n },\n}\n"},{"label":"Clipboard","name":"Clipboard","path":"../plugins/official/browser/actions/clipboard","type":"","author":"Delaney Gillilan","slug":"Copy text to the clipboard","description":"This action copies text to the clipboard using the Clipboard API.","icon":"mdi:clipboard","key":"static_library_source_plugins_official_browser_actions_clipboard_ts","contents":"// Authors: Delaney Gillilan\n// Icon: mdi:clipboard\n// Slug: Copy text to the clipboard\n// Description: This action copies text to the clipboard using the Clipboard API.\n\nimport { runtimeErr } from '../../../../engine/errors'\nimport { type ActionPlugin, PluginType } from '../../../../engine/types'\n\nexport const Clipboard: ActionPlugin = {\n type: PluginType.Action,\n name: 'clipboard',\n fn: (ctx, text) =\u003e {\n if (!navigator.clipboard) {\n throw runtimeErr('ClipboardNotAvailable', ctx)\n }\n navigator.clipboard.writeText(text)\n },\n}\n"},{"label":"CustomValidity","name":"CustomValidity","path":"../plugins/official/browser/attributes/customValidity","type":"","author":"Ben Croker","slug":"Add custom validity to an element using an expression","description":"This plugin allows you to add custom validity to an element using an expression. The expression should evaluate to a string that will be set as the custom validity message. This can be used to provide custom error messages for form validation.","icon":"mdi-message-alert","key":"static_library_source_plugins_official_browser_attributes_custom_validity_ts","contents":"// Authors: Ben Croker\n// Icon: mdi-message-alert\n// Slug: Add custom validity to an element using an expression\n// Description: This plugin allows you to add custom validity to an element using an expression. The expression should evaluate to a string that will be set as the custom validity message. This can be used to provide custom error messages for form validation.\n\nimport { runtimeErr } from '../../../../engine/errors'\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\n\nexport const CustomValidity: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'customValidity',\n keyReq: Requirement.Denied,\n valReq: Requirement.Must,\n onLoad: (ctx) =\u003e {\n const { el, genRX, effect } = ctx\n if (!(el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement)) {\n throw runtimeErr('CustomValidityInvalidElement', ctx)\n }\n const rx = genRX()\n return effect(() =\u003e {\n const result = rx\u003cstring\u003e()\n if (typeof result !== 'string') {\n throw runtimeErr('CustomValidityInvalidExpression', ctx, { result })\n }\n el.setCustomValidity(result)\n })\n },\n}\n"},{"label":"Intersects","name":"Intersects","path":"../plugins/official/browser/attributes/intersects","type":"","author":"Delaney Gillilan","slug":"Run expression when element intersects with viewport","description":"An attribute that runs an expression when the element intersects with the viewport.","icon":"mdi-light:vector-intersection","key":"static_library_source_plugins_official_browser_attributes_intersects_ts","contents":"// Authors: Delaney Gillilan\n// Icon: mdi-light:vector-intersection\n// Slug: Run expression when element intersects with viewport\n// Description: An attribute that runs an expression when the element intersects with the viewport.\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\n\nconst ONCE = 'once'\nconst HALF = 'half'\nconst FULL = 'full'\n\n// Run expression when element intersects with viewport\nexport const Intersects: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'intersects',\n keyReq: Requirement.Denied,\n mods: new Set([ONCE, HALF, FULL]),\n onLoad: ({ el, rawKey, mods, genRX }) =\u003e {\n const options = { threshold: 0 }\n if (mods.has(FULL)) options.threshold = 1\n else if (mods.has(HALF)) options.threshold = 0.5\n\n const rx = genRX()\n const observer = new IntersectionObserver((entries) =\u003e {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n rx()\n if (mods.has(ONCE)) {\n observer.disconnect()\n delete el.dataset[rawKey]\n }\n }\n }\n }, options)\n\n observer.observe(el)\n return () =\u003e observer.disconnect()\n },\n}\n"},{"label":"Persist","name":"Persist","path":"../plugins/official/browser/attributes/persist","type":"","author":"Delaney Gillilan","slug":"Persist data to local storage or session storage","description":"This plugin allows you to persist data to local storage or session storage. Once you add this attribute the data will be persisted to local storage or session storage.","icon":"mdi:floppy-variant","key":"static_library_source_plugins_official_browser_attributes_persist_ts","contents":"// Authors: Delaney Gillilan\n// Icon: mdi:floppy-variant\n// Slug: Persist data to local storage or session storage\n// Description: This plugin allows you to persist data to local storage or session storage. Once you add this attribute the data will be persisted to local storage or session storage.\n\nimport { DATASTAR } from '../../../../engine/consts'\nimport {\n type AttributePlugin,\n type NestedValues,\n PluginType,\n} from '../../../../engine/types'\nimport { modifyCasing, trimDollarSignPrefix } from '../../../../utils/text'\n\nconst SESSION = 'session'\n\nexport const Persist: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'persist',\n mods: new Set([SESSION]),\n onLoad: ({ key, effect, mods, signals, value }) =\u003e {\n key = modifyCasing(key, mods)\n if (key === '') {\n key = DATASTAR\n }\n\n const storage = mods.has(SESSION) ? sessionStorage : localStorage\n let paths = value.split(/\\s+/).filter((p) =\u003e p !== '')\n paths = paths.map((p) =\u003e trimDollarSignPrefix(p))\n\n const storageToSignals = () =\u003e {\n const data = storage.getItem(key) || '{}'\n const nestedValues = JSON.parse(data)\n signals.merge(nestedValues)\n }\n\n const signalsToStorage = () =\u003e {\n let nv: NestedValues\n if (!paths.length) {\n nv = signals.values()\n } else {\n nv = signals.subset(...paths)\n }\n storage.setItem(key, JSON.stringify(nv))\n }\n\n storageToSignals()\n return effect(() =\u003e {\n signalsToStorage()\n })\n },\n}\n"},{"label":"ReplaceUrl","name":"ReplaceUrl","path":"../plugins/official/browser/attributes/replaceUrl","type":"","author":"Delaney Gillilan","slug":"Replace the current URL with a new URL","description":"This plugin allows you to replace the current URL with a new URL. Once you add this attribute the current URL will be replaced with the new URL.","icon":"carbon:url","key":"static_library_source_plugins_official_browser_attributes_replace_url_ts","contents":"// Authors: Delaney Gillilan\n// Icon: carbon:url\n// Slug: Replace the current URL with a new URL\n// Description: This plugin allows you to replace the current URL with a new URL. Once you add this attribute the current URL will be replaced with the new URL.\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\n\nexport const ReplaceUrl: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'replaceUrl',\n keyReq: Requirement.Denied,\n valReq: Requirement.Must,\n onLoad: ({ effect, genRX }) =\u003e {\n const rx = genRX()\n return effect(() =\u003e {\n const url = rx\u003cstring\u003e()\n const baseUrl = window.location.href\n const fullUrl = new URL(url, baseUrl).toString()\n window.history.replaceState({}, '', fullUrl)\n })\n },\n}\n"},{"label":"ScrollIntoView","name":"ScrollIntoView","path":"../plugins/official/browser/attributes/scrollIntoView","type":"","author":"Delaney Gillilan","slug":"Scroll an element into view","description":"This attribute scrolls the element into view.","icon":"hugeicons:mouse-scroll-01","key":"static_library_source_plugins_official_browser_attributes_scroll_into_view_ts","contents":"// Authors: Delaney Gillilan\n// Icon: hugeicons:mouse-scroll-01\n// Slug: Scroll an element into view\n// Description: This attribute scrolls the element into view.\n\nimport { runtimeErr } from '../../../../engine/errors'\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\n\nconst SMOOTH = 'smooth'\nconst INSTANT = 'instant'\nconst AUTO = 'auto'\nconst HSTART = 'hstart'\nconst HCENTER = 'hcenter'\nconst HEND = 'hend'\nconst HNEAREST = 'hnearest'\nconst VSTART = 'vstart'\nconst VCENTER = 'vcenter'\nconst VEND = 'vend'\nconst VNEAREST = 'vnearest'\nconst FOCUS = 'focus'\n\nconst CENTER = 'center'\nconst START = 'start'\nconst END = 'end'\nconst NEAREST = 'nearest'\n\n// Scrolls the element into view\nexport const ScrollIntoView: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'scrollIntoView',\n keyReq: Requirement.Denied,\n valReq: Requirement.Denied,\n mods: new Set([\n SMOOTH,\n INSTANT,\n AUTO,\n HSTART,\n HCENTER,\n HEND,\n HNEAREST,\n VSTART,\n VCENTER,\n VEND,\n VNEAREST,\n FOCUS,\n ]),\n\n onLoad: (ctx) =\u003e {\n const { el, mods, rawKey } = ctx\n if (!el.tabIndex) el.setAttribute('tabindex', '0')\n const opts: ScrollIntoViewOptions = {\n behavior: SMOOTH,\n block: CENTER,\n inline: CENTER,\n }\n if (mods.has(SMOOTH)) opts.behavior = SMOOTH\n if (mods.has(INSTANT)) opts.behavior = INSTANT\n if (mods.has(AUTO)) opts.behavior = AUTO\n if (mods.has(HSTART)) opts.inline = START\n if (mods.has(HCENTER)) opts.inline = CENTER\n if (mods.has(HEND)) opts.inline = END\n if (mods.has(HNEAREST)) opts.inline = NEAREST\n if (mods.has(VSTART)) opts.block = START\n if (mods.has(VCENTER)) opts.block = CENTER\n if (mods.has(VEND)) opts.block = END\n if (mods.has(VNEAREST)) opts.block = NEAREST\n\n if (!(el instanceof HTMLElement || el instanceof SVGElement)) {\n throw runtimeErr('ScrollIntoViewInvalidElement', ctx)\n }\n if (!el.tabIndex) {\n el.setAttribute('tabindex', '0')\n }\n\n el.scrollIntoView(opts)\n if (mods.has('focus')) {\n el.focus()\n }\n\n delete el.dataset[rawKey]\n return () =\u003e {}\n },\n}\n"},{"label":"Show","name":"Show","path":"../plugins/official/browser/attributes/show","type":"","author":"Delaney Gillilan","slug":"Show or hide an element","description":"This attribute shows or hides an element based on the value of the expression. If the expression is true, the element is shown. If the expression is false, the element is hidden. The element is hidden by setting the display property to none.","icon":"streamline:interface-edit-view-eye-eyeball-open-view","key":"static_library_source_plugins_official_browser_attributes_show_ts","contents":"// Authors: Delaney Gillilan\n// Icon: streamline:interface-edit-view-eye-eyeball-open-view\n// Slug: Show or hide an element\n// Description: This attribute shows or hides an element based on the value of the expression. If the expression is true, the element is shown. If the expression is false, the element is hidden. The element is hidden by setting the display property to none.\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\n\nconst NONE = 'none'\nconst DISPLAY = 'display'\n\nexport const Show: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'show',\n keyReq: Requirement.Denied,\n valReq: Requirement.Must,\n onLoad: ({ el: { style: s }, genRX, effect }) =\u003e {\n const rx = genRX()\n return effect(async () =\u003e {\n const shouldShow = rx\u003cboolean\u003e()\n if (shouldShow) {\n if (s.display === NONE) {\n s.removeProperty(DISPLAY)\n }\n } else {\n s.setProperty(DISPLAY, NONE)\n }\n })\n },\n}\n"},{"label":"ViewTransition","name":"ViewTransition","path":"../plugins/official/browser/attributes/viewTransition","type":"","author":"Delaney Gillilan","slug":"Setup view transitions","description":"This attribute plugin sets up view transitions for the current view. This plugin requires the view transition API to be enabled in the browser. If the browser does not support view transitions, an error will be logged to the console.","icon":"material-symbols:masked-transitions","key":"static_library_source_plugins_official_browser_attributes_view_transition_ts","contents":"// Authors: Delaney Gillilan\n// Icon: material-symbols:masked-transitions\n// Slug: Setup view transitions\n// Description: This attribute plugin sets up view transitions for the current view. This plugin requires the view transition API to be enabled in the browser. If the browser does not support view transitions, an error will be logged to the console.\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { supportsViewTransitions } from '../../../../utils/view-transtions'\n\nconst VIEW_TRANSITION = 'view-transition'\n\nexport const ViewTransition: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'viewTransition',\n keyReq: Requirement.Denied,\n valReq: Requirement.Must,\n onGlobalInit() {\n let hasViewTransitionMeta = false\n for (const node of document.head.childNodes) {\n if (node instanceof HTMLMetaElement \u0026\u0026 node.name === VIEW_TRANSITION) {\n hasViewTransitionMeta = true\n }\n }\n\n if (!hasViewTransitionMeta) {\n const meta = document.createElement('meta')\n meta.name = VIEW_TRANSITION\n meta.content = 'same-origin'\n document.head.appendChild(meta)\n }\n },\n onLoad: ({ effect, el, genRX }) =\u003e {\n if (!supportsViewTransitions) {\n console.error('Browser does not support view transitions')\n return\n }\n const rx = genRX()\n return effect(() =\u003e {\n const name = rx\u003cstring\u003e()\n if (!name?.length) return\n const elVTASTyle = el.style as unknown as CSSStyleDeclaration\n elVTASTyle.viewTransitionName = name\n })\n },\n}\n"},{"label":"Attr","name":"Attr","path":"../plugins/official/dom/attributes/attr","type":"","author":"Delaney Gillilan","slug":"Bind attributes to expressions","description":"Any attribute can be bound to an expression. The attribute will be updated reactively whenever the expression signal changes.","icon":"akar-icons:link-chain","key":"static_library_source_plugins_official_dom_attributes_attr_ts","contents":"// Authors: Delaney Gillilan\n// Icon: akar-icons:link-chain\n// Slug: Bind attributes to expressions\n// Description: Any attribute can be bound to an expression. The attribute will be updated reactively whenever the expression signal changes.\n\nimport {\n type AttributePlugin,\n type NestedValues,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { kebab } from '../../../../utils/text'\n\nexport const Attr: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'attr',\n valReq: Requirement.Must,\n onLoad: ({ el, key, effect, genRX }) =\u003e {\n const rx = genRX()\n if (key === '') {\n return effect(async () =\u003e {\n const binds = rx\u003cNestedValues\u003e()\n for (const [attr, val] of Object.entries(binds)) {\n el.setAttribute(attr, val)\n }\n })\n }\n\n // Attributes are always kebab-case\n key = kebab(key)\n\n return effect(async () =\u003e {\n let value = false\n try {\n value = rx()\n } catch (e) {} //\n let v: string\n if (typeof value === 'string') {\n v = value\n } else {\n v = JSON.stringify(value)\n }\n if (!v || v === 'false' || v === 'null' || v === 'undefined') {\n el.removeAttribute(key)\n } else {\n el.setAttribute(key, v)\n }\n })\n },\n}\n"},{"label":"Bind","name":"Bind","path":"../plugins/official/dom/attributes/bind","type":"","author":"Delaney Gillilan","slug":"Bind attributes to expressions","description":"Any attribute can be bound to an expression. The attribute will be updated reactively whenever the expression signal changes.","icon":"akar-icons:link-chain","key":"static_library_source_plugins_official_dom_attributes_bind_ts","contents":"// Authors: Delaney Gillilan\n// Icon: akar-icons:link-chain\n// Slug: Bind attributes to expressions\n// Description: Any attribute can be bound to an expression. The attribute will be updated reactively whenever the expression signal changes.\n\nimport { runtimeErr } from '../../../../engine/errors'\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { modifyCasing, trimDollarSignPrefix } from '../../../../utils/text'\n\nconst dataURIRegex = /^data:(?\u003cmime\u003e[^;]+);base64,(?\u003ccontents\u003e.*)$/\nconst updateEvents = ['change', 'input', 'keydown']\n\nexport const Bind: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'bind',\n keyReq: Requirement.Exclusive,\n valReq: Requirement.Exclusive,\n onLoad: (ctx) =\u003e {\n const { el, key, mods, signals, value, effect } = ctx\n const signalName = key ? modifyCasing(key, mods) : trimDollarSignPrefix(value)\n\n let setFromSignal = () =\u003e {}\n let el2sig = () =\u003e {}\n\n const tnl = el.tagName.toLowerCase()\n let signalDefault: string | boolean | number | File = ''\n const isInput = tnl.includes('input')\n const type = el.getAttribute('type')\n const isCheckbox =\n tnl.includes('checkbox') || (isInput \u0026\u0026 type === 'checkbox')\n if (isCheckbox) {\n signalDefault = false\n }\n const isNumber = isInput \u0026\u0026 type === 'number'\n if (isNumber) {\n signalDefault = 0\n }\n const isSelect = tnl.includes('select')\n const isRadio = tnl.includes('radio') || (isInput \u0026\u0026 type === 'radio')\n const isFile = isInput \u0026\u0026 type === 'file'\n if (isFile) {\n // can't set a default value for a file input, yet\n }\n if (isRadio) {\n const name = el.getAttribute('name')\n if (!name?.length) {\n el.setAttribute('name', signalName)\n }\n }\n\n signals.upsertIfMissing(signalName, signalDefault)\n\n setFromSignal = () =\u003e {\n const hasValue = 'value' in el\n const v = signals.value(signalName)\n const vStr = `${v}`\n if (isCheckbox || isRadio) {\n const input = el as HTMLInputElement\n if (isCheckbox) {\n input.checked = !!v || v === 'true'\n } else if (isRadio) {\n // evaluate the value as string to handle any type casting\n // automatically since the attribute has to be a string anyways\n input.checked = vStr === input.value\n }\n } else if (isFile) {\n // File input reading from a signal is not supported yet\n } else if (isSelect) {\n const select = el as HTMLSelectElement\n if (select.multiple) {\n for (const opt of select.options) {\n if (opt?.disabled) return\n if (Array.isArray(v) || typeof v === 'string') {\n opt.selected = v.includes(opt.value)\n } else if (typeof v === 'number') {\n opt.selected = v === Number(opt.value)\n } else {\n opt.selected = v as boolean\n }\n }\n } else {\n select.value = vStr\n }\n } else if (hasValue) {\n el.value = vStr\n } else {\n el.setAttribute('value', vStr)\n }\n }\n\n el2sig = async () =\u003e {\n if (isFile) {\n const files = [...((el as HTMLInputElement)?.files || [])]\n const allContents: string[] = []\n const allMimes: string[] = []\n const allNames: string[] = []\n\n await Promise.all(\n files.map((f) =\u003e {\n return new Promise\u003cvoid\u003e((resolve) =\u003e {\n const reader = new FileReader()\n reader.onload = () =\u003e {\n if (typeof reader.result !== 'string') {\n throw runtimeErr('InvalidFileResultType', ctx, {\n resultType: typeof reader.result,\n })\n }\n const match = reader.result.match(dataURIRegex)\n if (!match?.groups) {\n throw runtimeErr('InvalidDataUri', ctx, {\n result: reader.result,\n })\n }\n allContents.push(match.groups.contents)\n allMimes.push(match.groups.mime)\n allNames.push(f.name)\n }\n reader.onloadend = () =\u003e resolve(void 0)\n reader.readAsDataURL(f)\n })\n }),\n )\n\n signals.setValue(signalName, allContents)\n signals.setValue(`${signalName}Mimes`, allMimes)\n signals.setValue(`${signalName}Names`, allNames)\n\n return\n }\n\n const current = signals.value(signalName)\n const input = (el as HTMLInputElement) || (el as HTMLElement)\n\n if (typeof current === 'number') {\n const v = Number(input.value || input.getAttribute('value'))\n signals.setValue(signalName, v)\n } else if (typeof current === 'string') {\n const v = input.value || input.getAttribute('value') || ''\n signals.setValue(signalName, v)\n } else if (typeof current === 'boolean') {\n if (isCheckbox) {\n const v = input.checked || input.getAttribute('checked') === 'true'\n signals.setValue(signalName, v)\n } else {\n const v = Boolean(input.value || input.getAttribute('value'))\n signals.setValue(signalName, v)\n }\n } else if (typeof current === 'undefined') {\n } else if (Array.isArray(current)) {\n // check if the input is a select element\n if (isSelect) {\n const select = el as HTMLSelectElement\n const selectedOptions = [...select.selectedOptions]\n const selectedValues = selectedOptions\n .filter((opt) =\u003e opt.selected)\n .map((opt) =\u003e opt.value)\n signals.setValue(signalName, selectedValues)\n } else {\n // assume it's a comma-separated string\n const v = JSON.stringify(input.value.split(','))\n signals.setValue(signalName, v)\n }\n } else {\n throw runtimeErr('BindUnsupportedSignalType', ctx, {\n signalType: typeof current,\n })\n }\n }\n\n for (const event of updateEvents) {\n el.addEventListener(event, el2sig)\n }\n const elSigClean = effect(() =\u003e setFromSignal())\n /*\n * The signal value needs to be updated after the \"pageshow\" event.\n * Sometimes, the browser might populate inputs with previous values\n * when navigating between pages using the back/forward navigation.\n *\n * For more information, read about bfcache:\n * https://web.dev/articles/bfcache\n */\n const onPageshow = (ev: PageTransitionEvent) =\u003e {\n if (!ev.persisted) return\n el2sig()\n }\n window.addEventListener(\"pageshow\", onPageshow)\n\n return () =\u003e {\n elSigClean()\n for (const event of updateEvents) {\n el.removeEventListener(event, el2sig)\n }\n window.removeEventListener(\"pageshow\", onPageshow)\n }\n },\n}\n"},{"label":"Class","name":"Class","path":"../plugins/official/dom/attributes/class","type":"","author":"Delaney Gillilan","slug":"Add or remove classes from an element reactively","description":"This action adds or removes classes from an element reactively based on the expression provided. The expression should be an object where the keys are the class names and the values are booleans. If the value is true, the class is added. If the value is false, the class is removed.","icon":"ic:baseline-format-paint","key":"static_library_source_plugins_official_dom_attributes_class_ts","contents":"// Authors: Delaney Gillilan\n// Icon: ic:baseline-format-paint\n// Slug: Add or remove classes from an element reactively\n// Description: This action adds or removes classes from an element reactively based on the expression provided. The expression should be an object where the keys are the class names and the values are booleans. If the value is true, the class is added. If the value is false, the class is removed.\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { modifyCasing } from '../../../../utils/text'\n\nexport const Class: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'class',\n valReq: Requirement.Must,\n onLoad: ({ el, key, mods, effect, genRX }) =\u003e {\n const cl = el.classList\n const rx = genRX()\n return effect(() =\u003e {\n if (key === '') {\n const classes = rx\u003cRecord\u003cstring, boolean\u003e\u003e()\n for (const [k, v] of Object.entries(classes)) {\n const classNames = k.split(/\\s+/)\n if (v) {\n cl.add(...classNames)\n } else {\n cl.remove(...classNames)\n }\n }\n } else {\n key = modifyCasing(key, mods)\n const shouldInclude = rx\u003cboolean\u003e()\n if (shouldInclude) {\n cl.add(key)\n } else {\n cl.remove(key)\n }\n }\n })\n },\n}\n"},{"label":"On","name":"On","path":"../plugins/official/dom/attributes/on","type":"","author":"Delaney Gillilan","slug":"Add an event listener to an element","description":"This action adds an event listener to an element. The event listener can be triggered by a variety of events, such as clicks, keypresses, and more. The event listener can also be set to trigger only once, or to be passive or capture. The event listener can also be debounced or throttled. The event listener can also be set to trigger only when the event target is outside the element.","icon":"material-symbols:mail","key":"static_library_source_plugins_official_dom_attributes_on_ts","contents":"// Authors: Delaney Gillilan\n// Icon: material-symbols:mail\n// Slug: Add an event listener to an element\n// Description: This action adds an event listener to an element. The event listener can be triggered by a variety of events, such as clicks, keypresses, and more. The event listener can also be set to trigger only once, or to be passive or capture. The event listener can also be debounced or throttled. The event listener can also be set to trigger only when the event target is outside the element.\n\nimport {\n type AttributePlugin,\n DATASTAR_SIGNAL_EVENT,\n type DatastarSignalEvent,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { tagHas, tagToMs } from '../../../../utils/tags'\nimport { camel, modifyCasing } from '../../../../utils/text'\nimport { debounce, delay, throttle } from '../../../../utils/timing'\nimport { supportsViewTransitions } from '../../../../utils/view-transtions'\nimport type { Signal } from '../../../../vendored/preact-core'\n\nconst EVT = 'evt'\nconst SIGNALS_CHANGE_PREFIX = 'signalsChange'\nconst signalChangeKeyLength = SIGNALS_CHANGE_PREFIX.length\n\nexport const On: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'on',\n keyReq: Requirement.Must,\n valReq: Requirement.Must,\n argNames: [EVT],\n removeOnLoad: (rawKey: string) =\u003e rawKey.startsWith('onLoad'),\n onLoad: ({ el, key, mods, rawKey, signals, value, effect, genRX }) =\u003e {\n const rx = genRX()\n let target: Element | Window | Document = el\n if (mods.has('window')) target = window\n\n let callback = (evt?: Event) =\u003e {\n if (evt) {\n // Always prevent default on submit events (because forms)\n if (mods.has('prevent') || key === 'submit') evt.preventDefault()\n if (mods.has('stop')) evt.stopPropagation()\n }\n rx(evt)\n }\n\n const delayArgs = mods.get('delay')\n if (delayArgs) {\n const wait = tagToMs(delayArgs)\n callback = delay(callback, wait)\n }\n\n const debounceArgs = mods.get('debounce')\n if (debounceArgs) {\n const wait = tagToMs(debounceArgs)\n const leading = tagHas(debounceArgs, 'leading', false)\n const trailing = !tagHas(debounceArgs, 'notrail', false)\n callback = debounce(callback, wait, leading, trailing)\n }\n\n const throttleArgs = mods.get('throttle')\n if (throttleArgs) {\n const wait = tagToMs(throttleArgs)\n const leading = !tagHas(throttleArgs, 'noleading', false)\n const trailing = tagHas(throttleArgs, 'trail', false)\n callback = throttle(callback, wait, leading, trailing)\n }\n\n if (mods.has('viewtransition') \u0026\u0026 supportsViewTransitions) {\n const cb = callback // I hate javascript\n callback = (...args: any[]) =\u003e\n document.startViewTransition(() =\u003e cb(...args))\n }\n\n const evtListOpts: AddEventListenerOptions = {\n capture: true,\n passive: false,\n once: false,\n }\n if (!mods.has('capture')) evtListOpts.capture = false\n if (mods.has('passive')) evtListOpts.passive = true\n if (mods.has('once')) evtListOpts.once = true\n\n if (key === 'load') {\n // Delay the callback to the next microtask so that indicators can be set\n setTimeout(() =\u003e callback(), 0)\n return () =\u003e {}\n }\n\n if (key === 'interval') {\n let duration = 1000\n const durationArgs = mods.get('duration')\n if (durationArgs) {\n duration = tagToMs(durationArgs)\n const leading = tagHas(durationArgs, 'leading', false)\n if (leading) {\n // Remove `.leading` from the dataset so the callback is only ever called on page load\n el.dataset[rawKey.replace('.leading', '')] = value\n delete el.dataset[rawKey]\n callback()\n }\n }\n const intervalId = setInterval(callback, duration)\n\n return () =\u003e {\n clearInterval(intervalId)\n }\n }\n\n if (key === 'raf') {\n let rafId: number | undefined\n const raf = () =\u003e {\n callback()\n rafId = requestAnimationFrame(raf)\n }\n rafId = requestAnimationFrame(raf)\n\n return () =\u003e {\n if (rafId) cancelAnimationFrame(rafId)\n }\n }\n\n if (key.startsWith(SIGNALS_CHANGE_PREFIX)) {\n if (key === SIGNALS_CHANGE_PREFIX) {\n callback()\n const signalFn = (event: CustomEvent\u003cDatastarSignalEvent\u003e) =\u003e\n callback(event)\n document.addEventListener(DATASTAR_SIGNAL_EVENT, signalFn)\n return () =\u003e {\n document.removeEventListener(DATASTAR_SIGNAL_EVENT, signalFn)\n }\n }\n\n const signalPath = modifyCasing(\n camel(key.slice(signalChangeKeyLength)),\n mods,\n )\n const signalValues = new Map\u003cSignal, any\u003e()\n signals.walk((path, signal) =\u003e {\n if (path.startsWith(signalPath)) {\n signalValues.set(signal, signal.value)\n }\n })\n return effect(() =\u003e {\n for (const [signal, prev] of signalValues) {\n if (prev !== signal.value) {\n callback()\n signalValues.set(signal, signal.value)\n }\n }\n })\n }\n\n const testOutside = mods.has('outside')\n if (testOutside) {\n target = document\n const cb = callback\n const targetOutsideCallback = (e?: Event) =\u003e {\n const targetHTML = e?.target as HTMLElement\n if (!el.contains(targetHTML)) {\n cb(e)\n }\n }\n callback = targetOutsideCallback\n }\n\n const eventName = modifyCasing(key, mods)\n target.addEventListener(eventName, callback, evtListOpts)\n return () =\u003e {\n target.removeEventListener(eventName, callback)\n }\n },\n}\n"},{"label":"Ref","name":"Ref","path":"../plugins/official/dom/attributes/ref","type":"","author":"Delaney Gillilan","slug":"Create a reference to an element","description":"This attribute creates a reference to an element that can be used in other expressions.","icon":"mdi:cursor-pointer","key":"static_library_source_plugins_official_dom_attributes_ref_ts","contents":"// Authors: Delaney Gillilan\n// Icon: mdi:cursor-pointer\n// Slug: Create a reference to an element\n// Description: This attribute creates a reference to an element that can be used in other expressions.\n\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\nimport { modifyCasing, trimDollarSignPrefix } from '../../../../utils/text'\n\n// Sets the value of the element\nexport const Ref: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'ref',\n keyReq: Requirement.Exclusive,\n valReq: Requirement.Exclusive,\n onLoad: ({ el, key, mods, signals, value }) =\u003e {\n const signalName = key ? modifyCasing(key, mods) : trimDollarSignPrefix(value)\n signals.setValue(signalName, el)\n },\n}\n"},{"label":"Text","name":"Text","path":"../plugins/official/dom/attributes/text","type":"","author":"Delaney Gillilan","slug":"Set the text content of an element","description":"This attribute sets the text content of an element to the result of the expression.","icon":"tabler:typography","key":"static_library_source_plugins_official_dom_attributes_text_ts","contents":"// Authors: Delaney Gillilan\n// Icon: tabler:typography\n// Slug: Set the text content of an element\n// Description: This attribute sets the text content of an element to the result of the expression.\n\nimport { runtimeErr } from '../../../../engine/errors'\nimport {\n type AttributePlugin,\n PluginType,\n Requirement,\n} from '../../../../engine/types'\n\nexport const Text: AttributePlugin = {\n type: PluginType.Attribute,\n name: 'text',\n keyReq: Requirement.Denied,\n valReq: Requirement.Must,\n onLoad: (ctx) =\u003e {\n const { el, effect, genRX } = ctx\n const rx = genRX()\n if (!(el instanceof HTMLElement)) {\n runtimeErr('TextInvalidElement', ctx)\n }\n return effect(() =\u003e {\n const res = rx(ctx)\n el.textContent = `${res}`\n })\n },\n}\n"},{"label":"Fit","name":"Fit","path":"../plugins/official/logic/actions/fit","type":"","author":"Delaney Gillilan","slug":"Clamp a value to a new range","description":"This action clamps a value to a new range. The value is first scaled to the new range, then clamped to the new range. This is useful for scaling a value to a new range, then clamping it to that range.","icon":"material-symbols:fit-screen-outline","key":"static_library_source_plugins_official_logic_actions_fit_ts","contents":"// Authors: Delaney Gillilan\n// Icon: material-symbols:fit-screen-outline\n// Slug: Clamp a value to a new range\n// Description: This action clamps a value to a new range. The value is first scaled to the new range, then clamped to the new range. This is useful for scaling a value to a new range, then clamping it to that range.\n\nimport {\n type ActionPlugin,\n PluginType,\n type RuntimeContext,\n} from '../../../../engine/types'\n\nconst { round, max, min } = Math\nexport const Fit: ActionPlugin = {\n type: PluginType.Action,\n name: 'fit',\n fn: (\n _: RuntimeContext,\n v: number,\n oldMin: number,\n oldMax: number,\n newMin: number,\n newMax: number,\n shouldClamp = false,\n shouldRound = false,\n ) =\u003e {\n let fitted = ((v - oldMin) / (oldMax - oldMin)) * (newMax - newMin) + newMin\n if (shouldRound) {\n fitted = round(fitted)\n }\n if (shouldClamp) {\n fitted = max(newMin, min(newMax, fitted))\n }\n return fitted\n },\n}\n"},{"label":"SetAll","name":"SetAll","path":"../plugins/official/logic/actions/setAll","type":"","author":"Delaney Gillilan","slug":"Set all signals that match a regular expression","description":"","icon":"ion:checkmark-round","key":"static_library_source_plugins_official_logic_actions_set_all_ts","contents":"// Authors: Delaney Gillilan\n// Icon: ion:checkmark-round\n// Slug: Set all signals that match a regular expression\n\nimport { type ActionPlugin, PluginType } from '../../../../engine/types'\n\nexport const SetAll: ActionPlugin = {\n type: PluginType.Action,\n name: 'setAll',\n fn: ({ signals }, prefix: string, newValue) =\u003e {\n signals.walk((path, signal) =\u003e {\n if (!path.startsWith(prefix)) return\n signal.value = newValue\n })\n },\n}\n"},{"label":"ToggleAll","name":"ToggleAll","path":"../plugins/official/logic/actions/toggleAll","type":"","author":"Delaney Gillilan","slug":"Toggle all signals that match a regular expression","description":"","icon":"material-symbols:toggle-off","key":"static_library_source_plugins_official_logic_actions_toggle_all_ts","contents":"// Authors: Delaney Gillilan\n// Icon: material-symbols:toggle-off\n// Slug: Toggle all signals that match a regular expression\n\nimport { type ActionPlugin, PluginType } from '../../../../engine/types'\n\nexport const ToggleAll: ActionPlugin = {\n type: PluginType.Action,\n name: 'toggleAll',\n fn: ({ signals }, prefix: string) =\u003e {\n signals.walk((path, signal) =\u003e {\n if (!path.startsWith(prefix)) return\n signal.value = !signal.value\n })\n },\n}\n"}],"alias":""}} |