{"version":3,"file":"collection-primary-page-section.min.js","mappings":"wZAsBA,SAASA,EAAuBC,GAAmB,IAAAC,EACjD,MAAMC,EAAqB,IAAIC,MAAM,gCAAiC,CACpEC,SAAS,EACTC,UAAU,IAEO,OAAnBJ,EAAAD,EAAMM,gBAANL,EAAqBM,cAAcL,EACrC,CAUA,SAASM,EAAqBR,GAC5B,GAAIA,EAAMM,yBAAyBG,aAAeT,EAAMM,cAAcI,QAAQC,WAAY,CACxF,MAAMC,EAAM,IAAIC,IAAIC,SAASC,MAC7BH,EAAII,aAAaC,OAAOjB,EAAMM,cAAcI,QAAQC,WAClDX,EAAMM,cAAcI,QAAQQ,aAC9BJ,SAASC,KAAOH,EAAIO,UACtB,CACF,CAEA,SAASC,EAAsBC,GAC7B,MAAMT,EAAM,IAAIC,IAAIC,SAASC,MAOvBO,EAAO,IAAIV,EAAII,aAAaM,QAMlC,IAAK,MAAMC,KAAOD,GACJ,YAARC,GAAqBA,EAAIC,WAAW,aACtCZ,EAAII,aAAaC,OAAOM,GAI5BT,SAASC,KAAOH,EAAIO,UACtB,ECVO,SAAeM,EACpBC,GACA,QAD2B,IAA3BA,IAAAA,EAA8B,oBACF,aAAxBC,SAASC,YAAuC,qBAATF,GACjB,gBAAxBC,SAASC,WAA+B,CACxC,MAAMC,EAAQC,WAAWL,EAAU,GAEnC,MAD8B,IAAMM,aAAaF,EAEnD,CACEG,iBAAiBN,EAAMD,EAI3B,CDAAQ,CApEA,WACE,MAAMC,EAAgBP,SAASQ,cAA2B,uCAC7C,MAAbD,GAAAA,EAAeF,iBAAiB,QAASjC,GAEzC,MAAMqC,EAAQT,SAASU,iBAA8B,6BACrD,IAAK,MAAMC,KAAQF,EACjBE,EAAKN,iBAAiB,QAASxB,GAGjC,MAAM+B,EAAcZ,SAASQ,cAA2B,8BAEpDI,GACFA,EAAYP,iBAAiB,QAASZ,EAE1C","sources":["webpack://lange/./sections/collection-primary-page/index.ts","webpack://lange/./lib/dom-ready.ts"],"sourcesContent":["import { ready } from '../../lib/dom-ready.js';\n\nfunction onReady() {\n  const filtersButton = document.querySelector<HTMLElement>('.collection-primary .actions button');\n  filtersButton?.addEventListener('click', onFiltersButtonClicked);\n\n  const pills = document.querySelectorAll<HTMLElement>('.collection-primary .pill');\n  for (const pill of pills) {\n    pill.addEventListener('click', onFiltersPillClicked);\n  }\n\n  const clearButton = document.querySelector<HTMLElement>('.collection-primary .clear');\n  // the clear all button does not exist if there are no active filters\n  if (clearButton) {\n    clearButton.addEventListener('click', onClearFiltersClicked);\n  }\n}\n\n/**\n * Requests that the filters drawer open. This section does not own the drawer. It dispatches a\n * request from the clicked button and the filters drawer section handles opening itself.\n */\nfunction onFiltersButtonClicked(event: MouseEvent) {\n  const openRequestedEvent = new Event('filters-drawer-open-requested', {\n    bubbles: true,\n    composed: true\n  });\n  event.currentTarget?.dispatchEvent(openRequestedEvent);\n}\n\n/**\n * Redirect to a modified url in response to a click.\n *\n * Shopify's multi-select filters serialize as repeated params with the same name (e.g.\n * filter.v.option.color=Red&filter.v.option.color=Blue), so we pass the value as the second\n * argument to delete only this pill's specific name=value pair. The one-argument form would remove\n * every value in this filter dimension, dismissing the other selected pills too.\n */\nfunction onFiltersPillClicked(event: MouseEvent) {\n  if (event.currentTarget instanceof HTMLElement && event.currentTarget.dataset.filterName) {\n    const url = new URL(location.href);\n    url.searchParams.delete(event.currentTarget.dataset.filterName,\n      event.currentTarget.dataset.filterValue);\n    location.href = url.toString();\n  }\n}\n\nfunction onClearFiltersClicked(_event: MouseEvent) {\n  const url = new URL(location.href);\n\n  // The creation of a new array \"keys\" from url.searchParams.keys() is necessary because we're\n  // modifying \"url.searchParams\" during its iteration in the for loop. Modifying a collection\n  // during iteration can result in unexpected behavior or errors due to index misalignment. By\n  // creating and iterating over a separate array \"keys\", we avoid these issues.\n\n  const keys = [...url.searchParams.keys()];\n\n  // We should only remove the search parameters that are related to filters and sorting. In\n  // Shopify, \"sort_by\" parameter is dedicated for sorting. All filter parameters start with\n  // \"filter.\".\n\n  for (const key of keys) {\n    if (key === 'sort_by' || key.startsWith('filter.')) {\n      url.searchParams.delete(key);\n    }\n  }\n\n  location.href = url.toString();\n}\n\nready(onReady);\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n"],"names":["onFiltersButtonClicked","event","_event$currentTarget","openRequestedEvent","Event","bubbles","composed","currentTarget","dispatchEvent","onFiltersPillClicked","HTMLElement","dataset","filterName","url","URL","location","href","searchParams","delete","filterValue","toString","onClearFiltersClicked","_event","keys","key","startsWith","callback","type","document","readyState","timer","setTimeout","clearTimeout","addEventListener","ready","filtersButton","querySelector","pills","querySelectorAll","pill","clearButton"],"sourceRoot":""}