@ozankurt/file-chips 0.0.0-stage → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../js/popover.js", "../js/marks.js", "../js/index.js", "../js/browser.js"],
4
+ "sourcesContent": ["/**\n * Popover open/close state and viewport-aware positioning.\n *\n * Popovers stay in place in the DOM, next to their chip, so they inherit the\n * chip container's CSS variables, follow it in tab order, and disappear with\n * it when the page re-renders. They are position: fixed; position() corrects\n * for ancestors (transforms, filters) that move the fixed origin.\n */\n\nconst GAP = 8;\nconst EDGE = 8;\n\nlet openPopover = null;\nlet openChip = null;\n\nconst clamp = (value, min, max) => Math.max(min, Math.min(max, value));\n\nconst popoverFor = (chip) => chip.ownerDocument.getElementById(chip.dataset.fileChip);\n\n/**\n * Place the popover below the chip, flip it above when there is no room,\n * and keep it inside the viewport.\n */\nconst position = (chip, popover) => {\n const rect = chip.getBoundingClientRect();\n const width = popover.offsetWidth;\n const height = popover.offsetHeight;\n\n let top = rect.bottom + GAP;\n const left = clamp(rect.left, EDGE, Math.max(EDGE, window.innerWidth - width - EDGE));\n\n if (top + height > window.innerHeight) {\n const above = rect.top - height - GAP;\n if (above >= EDGE) {\n top = above;\n }\n }\n\n top = clamp(top, EDGE, window.innerHeight - height - EDGE);\n\n // Where does (0, 0) land? Non-zero when a transformed ancestor becomes\n // the containing block of this fixed element.\n popover.style.top = '0px';\n popover.style.left = '0px';\n const origin = popover.getBoundingClientRect();\n\n popover.style.top = `${Math.round(top - origin.top)}px`;\n popover.style.left = `${Math.round(left - origin.left)}px`;\n};\n\n/**\n * Close the open popover. With restoreFocus, focus that was inside the\n * popover goes back to its chip.\n */\nconst close = ({ restoreFocus = false } = {}) => {\n if (!openPopover) {\n return;\n }\n\n const popover = openPopover;\n const chip = openChip;\n const hadFocus = popover.contains(popover.ownerDocument.activeElement);\n\n popover.hidden = true;\n chip?.setAttribute('aria-expanded', 'false');\n openPopover = null;\n openChip = null;\n\n if (restoreFocus && hadFocus) {\n chip?.focus();\n }\n};\n\n/**\n * Open the popover that belongs to a chip. Returns the popover, or null when\n * the chip has none.\n */\nconst open = (chip) => {\n close();\n\n const popover = popoverFor(chip);\n if (!popover) {\n return null;\n }\n\n popover.hidden = false;\n chip.setAttribute('aria-expanded', 'true');\n openPopover = popover;\n openChip = chip;\n\n position(chip, popover);\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => openPopover === popover && position(chip, popover));\n }\n\n return popover;\n};\n\nconst getOpen = () => openPopover;\n\nconst getOpenChip = () => openChip;\n\nexport { open, close, getOpen, getOpenChip, position, popoverFor };\n", "/**\n * Mark-for-removal: toggles a hidden input carrying the file id so the\n * server can delete the file when the form is saved.\n */\n\nconst DEFAULT_MARKED = 'Marked';\nconst DEFAULT_UNMARKED = 'Remove on save';\n\n/**\n * The element hidden inputs go into: the [data-file-chips-bucket] of the\n * chip's [data-file-chips] container, otherwise the chip's form.\n */\nconst targetFor = (chip) => chip.closest('[data-file-chips]')?.querySelector('[data-file-chips-bucket]') ?? chip.closest('form');\n\n// Compare in JS instead of building a CSS selector, so ids and input\n// names containing quotes or brackets cannot break the lookup.\nconst findInput = (target, name, id) => [...target.querySelectorAll('input[type=\"hidden\"]')]\n .find((input) => input.name === name && input.value === id) ?? null;\n\nconst render = (chip, button, marked) => {\n chip.classList.toggle('fc-chip--marked', marked);\n button.classList.toggle('fc-btn--active', marked);\n button.setAttribute('aria-pressed', String(marked));\n\n const label = button.querySelector('[data-file-chip-remove-label]') ?? button;\n label.textContent = marked\n ? (button.dataset.labelMarked ?? DEFAULT_MARKED)\n : (button.dataset.labelUnmarked ?? DEFAULT_UNMARKED);\n};\n\nconst buttonIn = (popover) => popover.querySelector('[data-file-chip-remove]');\n\n/**\n * Reflect the current mark state of a chip in its popover button.\n */\nconst sync = (chip, popover) => {\n const button = buttonIn(popover);\n const target = targetFor(chip);\n if (!button || !target) {\n return;\n }\n\n render(chip, button, findInput(target, button.dataset.fileChipInput, button.dataset.fileChipId) !== null);\n};\n\n/**\n * Toggle the mark of the chip whose popover holds this button.\n */\nconst toggle = (chip, button) => {\n const target = targetFor(chip);\n if (!target) {\n console.warn('[FileChips] No [data-file-chips-bucket] or <form> found for this chip; cannot mark it for removal.');\n return;\n }\n\n const name = button.dataset.fileChipInput;\n const id = button.dataset.fileChipId;\n const existing = findInput(target, name, id);\n\n if (existing) {\n existing.remove();\n } else {\n const input = chip.ownerDocument.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = id;\n target.appendChild(input);\n }\n\n const marked = !existing;\n render(chip, button, marked);\n chip.dispatchEvent(new CustomEvent('file-chips:change', { bubbles: true, detail: { id, input: name, marked } }));\n};\n\nexport { sync, toggle };\n", "/**\n * File Chips\n *\n * Compact file chips with a preview popover and mark-for-removal.\n * Markup contract (see README):\n * [data-file-chips] container\n * [data-file-chips-bucket] optional element that receives hidden inputs\n * [data-file-chip=\"<id>\"] chip, points at its popover by id\n * [data-file-chip-popover] popover panel (starts hidden)\n * [data-file-chip-remove] toggle button with data-file-chip-id and data-file-chip-input\n */\n\nimport { close, getOpen, getOpenChip, open, position } from './popover.js';\nimport { sync, toggle } from './marks.js';\n\n// One set of document listeners per document, shared by every root on it,\n// so several roots never handle the same event twice.\nconst documents = new Map();\n\nconst handled = (doc, element) => [...(documents.get(doc)?.roots ?? [])].some((root) => root.contains(element));\n\nconst openWithState = (chip) => {\n const popover = open(chip);\n if (popover) {\n sync(chip, popover);\n }\n\n return popover;\n};\n\nconst listenersFor = (doc) => ({\n click(event) {\n const target = event.target;\n\n const remove = target.closest?.('[data-file-chip-remove]');\n if (remove && getOpen()?.contains(remove)) {\n toggle(getOpenChip(), remove);\n return;\n }\n\n const chip = target.closest?.('[data-file-chip]');\n if (!chip || !handled(doc, chip)) {\n return;\n }\n\n event.preventDefault();\n\n if (getOpenChip() === chip) {\n close();\n } else {\n openWithState(chip);\n }\n },\n\n mousedown(event) {\n const popover = getOpen();\n if (popover && !popover.contains(event.target) && !event.target.closest?.('[data-file-chip]')) {\n close();\n }\n },\n\n keydown(event) {\n if (event.key === 'Escape' && getOpen()) {\n close({ restoreFocus: true });\n }\n },\n\n reposition() {\n const popover = getOpen();\n if (popover) {\n position(getOpenChip(), popover);\n }\n },\n});\n\nconst listen = (doc, listeners, method) => {\n doc[method]('click', listeners.click);\n doc[method]('mousedown', listeners.mousedown);\n doc[method]('keydown', listeners.keydown);\n doc.defaultView?.[method]('resize', listeners.reposition);\n doc.defaultView?.[method]('scroll', listeners.reposition, true);\n};\n\n/**\n * Start handling chips inside `root` (default: the whole document). A root\n * that is already handled, itself or through an ancestor, is a no-op.\n * Returns a function that stops handling this root.\n */\nconst init = ({ root = document } = {}) => {\n const doc = root.ownerDocument ?? root;\n\n if (handled(doc, root)) {\n return () => {};\n }\n\n if (!documents.has(doc)) {\n const listeners = listenersFor(doc);\n documents.set(doc, { roots: new Set(), listeners });\n listen(doc, listeners, 'addEventListener');\n }\n\n const entry = documents.get(doc);\n entry.roots.add(root);\n\n return () => {\n if (!entry.roots.delete(root)) {\n return;\n }\n\n if (getOpenChip() && root.contains(getOpenChip())) {\n close();\n }\n\n if (entry.roots.size === 0) {\n listen(doc, entry.listeners, 'removeEventListener');\n documents.delete(doc);\n }\n };\n};\n\nconst FileChips = {\n init,\n open: openWithState,\n close: () => close(),\n getOpen,\n position,\n};\n\nexport { init, close, getOpen, position };\nexport default FileChips;\n", "/**\n * Script-tag entry: exposes window.FileChips and starts it on the whole\n * document once the DOM is ready. Add data-manual to the <script> tag to\n * skip the automatic start and call FileChips.init() yourself.\n */\n\nimport FileChips from './index.js';\n\nwindow.FileChips = FileChips;\n\nconst script = document.currentScript;\n\nif (!script?.hasAttribute('data-manual')) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => FileChips.init(), { once: true });\n } else {\n FileChips.init();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ :root{--fc-font-size: .875rem;--fc-gap: .5rem;--fc-radius: .5rem;--fc-chip-max-width: 220px;--fc-chip-bg: #fff;--fc-chip-bg-hover: rgba(0, 0, 0, .03);--fc-border: rgba(0, 0, 0, .1);--fc-text: inherit;--fc-muted: #6b7280;--fc-danger: #dc3545;--fc-danger-bg: rgba(220, 53, 69, .06);--fc-primary: #2563eb;--fc-primary-text: #fff;--fc-focus: rgba(37, 99, 235, .35);--fc-ext-bg: rgba(0, 0, 0, .06);--fc-popover-width: 520px;--fc-popover-bg: #fff;--fc-popover-border: rgba(0, 0, 0, .12);--fc-popover-radius: .75rem;--fc-popover-shadow: 0 10px 30px rgba(0, 0, 0, .15);--fc-popover-z: 1080;--fc-thumb-size: 80px;--fc-thumb-bg: #fafafa}.fc-chips{display:flex;flex-wrap:wrap;gap:var(--fc-gap)}.fc-chip{display:inline-flex;align-items:center;gap:.375rem;max-width:var(--fc-chip-max-width);padding:.375rem .5rem;border:1px solid var(--fc-border);border-radius:var(--fc-radius);background:var(--fc-chip-bg);color:var(--fc-text);font:inherit;font-size:var(--fc-font-size);line-height:1.25;text-decoration:none;cursor:pointer;transition:background .15s,border-color .15s,box-shadow .15s}.fc-chip:hover{background:var(--fc-chip-bg-hover)}.fc-chip:focus-visible,.fc-btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--fc-focus)}.fc-chip--marked{border-color:var(--fc-danger);background:var(--fc-danger-bg)}.fc-chip--marked .fc-chip__name{text-decoration:line-through;opacity:.8}.fc-chip__name,.fc-popover__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-chip__ext{flex:none;padding:.0625rem .3125rem;border-radius:.25rem;background:var(--fc-ext-bg);font-size:.6875rem;font-weight:600;letter-spacing:.02em;text-transform:uppercase}.fc-popover{position:fixed;z-index:var(--fc-popover-z);width:var(--fc-popover-width);max-width:92vw;padding:1rem;border:1px solid var(--fc-popover-border);border-radius:var(--fc-popover-radius);background:var(--fc-popover-bg);box-shadow:var(--fc-popover-shadow);color:var(--fc-text);font-size:var(--fc-font-size)}.fc-popover[hidden]{display:none}.fc-popover__head{display:flex;align-items:flex-start;gap:.75rem}.fc-popover__info{min-width:0;flex:1}.fc-popover__title{margin-bottom:.25rem;font-weight:600}.fc-popover__meta,.fc-popover__excerpt{color:var(--fc-muted);font-size:.8125rem}.fc-popover__meta{display:flex;flex-wrap:wrap;gap:.5rem}.fc-popover__excerpt{margin-top:.5rem}.fc-popover__actions{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin-top:.75rem}.fc-popover__actions [data-file-chip-remove]{margin-left:auto}.fc-thumb{display:flex;flex:none;align-items:center;justify-content:center;width:var(--fc-thumb-size);height:var(--fc-thumb-size);overflow:hidden;border:1px solid var(--fc-border);border-radius:var(--fc-radius);background:var(--fc-thumb-bg)}.fc-thumb img{display:block;max-width:100%;max-height:100%}.fc-thumb .fc-chip__ext{font-size:.875rem}.fc-btn{display:inline-flex;align-items:center;gap:.25rem;padding:.25rem .625rem;border:1px solid transparent;border-radius:.375rem;background:transparent;color:inherit;font:inherit;font-size:.8125rem;line-height:1.5;text-decoration:none;cursor:pointer}.fc-btn--primary{border-color:var(--fc-primary);background:var(--fc-primary);color:var(--fc-primary-text)}.fc-btn--outline{border-color:var(--fc-primary);color:var(--fc-primary)}.fc-btn--danger{border-color:var(--fc-danger);color:var(--fc-danger)}.fc-btn--danger.fc-btn--active{background:var(--fc-danger);color:#fff}
@@ -0,0 +1,2 @@
1
+ (()=>{var c=null,m=null,E=(t,e,n)=>Math.max(e,Math.min(n,t)),b=t=>t.ownerDocument.getElementById(t.dataset.fileChip),f=(t,e)=>{let n=t.getBoundingClientRect(),o=e.offsetWidth,s=e.offsetHeight,i=n.bottom+8,p=E(n.left,8,Math.max(8,window.innerWidth-o-8));if(i+s>window.innerHeight){let g=n.top-s-8;g>=8&&(i=g)}i=E(i,8,window.innerHeight-s-8),e.style.top="0px",e.style.left="0px";let r=e.getBoundingClientRect();e.style.top=`${Math.round(i-r.top)}px`,e.style.left=`${Math.round(p-r.left)}px`},a=({restoreFocus:t=!1}={})=>{if(!c)return;let e=c,n=m,o=e.contains(e.ownerDocument.activeElement);e.hidden=!0,n?.setAttribute("aria-expanded","false"),c=null,m=null,t&&o&&n?.focus()},w=t=>{a();let e=b(t);return e?(e.hidden=!1,t.setAttribute("aria-expanded","true"),c=e,m=t,f(t,e),typeof requestAnimationFrame=="function"&&requestAnimationFrame(()=>c===e&&f(t,e)),e):null},l=()=>c,d=()=>m;var M="Marked",G="Remove on save",y=t=>t.closest("[data-file-chips]")?.querySelector("[data-file-chips-bucket]")??t.closest("form"),v=(t,e,n)=>[...t.querySelectorAll('input[type="hidden"]')].find(o=>o.name===e&&o.value===n)??null,C=(t,e,n)=>{t.classList.toggle("fc-chip--marked",n),e.classList.toggle("fc-btn--active",n),e.setAttribute("aria-pressed",String(n));let o=e.querySelector("[data-file-chip-remove-label]")??e;o.textContent=n?e.dataset.labelMarked??M:e.dataset.labelUnmarked??G},S=t=>t.querySelector("[data-file-chip-remove]"),D=(t,e)=>{let n=S(e),o=y(t);!n||!o||C(t,n,v(o,n.dataset.fileChipInput,n.dataset.fileChipId)!==null)},x=(t,e)=>{let n=y(t);if(!n){console.warn("[FileChips] No [data-file-chips-bucket] or <form> found for this chip; cannot mark it for removal.");return}let o=e.dataset.fileChipInput,s=e.dataset.fileChipId,i=v(n,o,s);if(i)i.remove();else{let r=t.ownerDocument.createElement("input");r.type="hidden",r.name=o,r.value=s,n.appendChild(r)}let p=!i;C(t,e,p),t.dispatchEvent(new CustomEvent("file-chips:change",{bubbles:!0,detail:{id:s,input:o,marked:p}}))};var u=new Map,F=(t,e)=>[...u.get(t)?.roots??[]].some(n=>n.contains(e)),k=t=>{let e=w(t);return e&&D(t,e),e},L=t=>({click(e){let n=e.target,o=n.closest?.("[data-file-chip-remove]");if(o&&l()?.contains(o)){x(d(),o);return}let s=n.closest?.("[data-file-chip]");!s||!F(t,s)||(e.preventDefault(),d()===s?a():k(s))},mousedown(e){let n=l();n&&!n.contains(e.target)&&!e.target.closest?.("[data-file-chip]")&&a()},keydown(e){e.key==="Escape"&&l()&&a({restoreFocus:!0})},reposition(){let e=l();e&&f(d(),e)}}),A=(t,e,n)=>{t[n]("click",e.click),t[n]("mousedown",e.mousedown),t[n]("keydown",e.keydown),t.defaultView?.[n]("resize",e.reposition),t.defaultView?.[n]("scroll",e.reposition,!0)},I=({root:t=document}={})=>{let e=t.ownerDocument??t;if(F(e,t))return()=>{};if(!u.has(e)){let o=L(e);u.set(e,{roots:new Set,listeners:o}),A(e,o,"addEventListener")}let n=u.get(e);return n.roots.add(t),()=>{n.roots.delete(t)&&(d()&&t.contains(d())&&a(),n.roots.size===0&&(A(e,n.listeners,"removeEventListener"),u.delete(e)))}},q={init:I,open:k,close:()=>a(),getOpen:l,position:f};var h=q;window.FileChips=h;var O=document.currentScript;O?.hasAttribute("data-manual")||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>h.init(),{once:!0}):h.init());})();
2
+ //# sourceMappingURL=file-chips.min.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../js/popover.js", "../js/marks.js", "../js/index.js", "../js/browser.js"],
4
+ "sourcesContent": ["/**\n * Popover open/close state and viewport-aware positioning.\n *\n * Popovers stay in place in the DOM, next to their chip, so they inherit the\n * chip container's CSS variables, follow it in tab order, and disappear with\n * it when the page re-renders. They are position: fixed; position() corrects\n * for ancestors (transforms, filters) that move the fixed origin.\n */\n\nconst GAP = 8;\nconst EDGE = 8;\n\nlet openPopover = null;\nlet openChip = null;\n\nconst clamp = (value, min, max) => Math.max(min, Math.min(max, value));\n\nconst popoverFor = (chip) => chip.ownerDocument.getElementById(chip.dataset.fileChip);\n\n/**\n * Place the popover below the chip, flip it above when there is no room,\n * and keep it inside the viewport.\n */\nconst position = (chip, popover) => {\n const rect = chip.getBoundingClientRect();\n const width = popover.offsetWidth;\n const height = popover.offsetHeight;\n\n let top = rect.bottom + GAP;\n const left = clamp(rect.left, EDGE, Math.max(EDGE, window.innerWidth - width - EDGE));\n\n if (top + height > window.innerHeight) {\n const above = rect.top - height - GAP;\n if (above >= EDGE) {\n top = above;\n }\n }\n\n top = clamp(top, EDGE, window.innerHeight - height - EDGE);\n\n // Where does (0, 0) land? Non-zero when a transformed ancestor becomes\n // the containing block of this fixed element.\n popover.style.top = '0px';\n popover.style.left = '0px';\n const origin = popover.getBoundingClientRect();\n\n popover.style.top = `${Math.round(top - origin.top)}px`;\n popover.style.left = `${Math.round(left - origin.left)}px`;\n};\n\n/**\n * Close the open popover. With restoreFocus, focus that was inside the\n * popover goes back to its chip.\n */\nconst close = ({ restoreFocus = false } = {}) => {\n if (!openPopover) {\n return;\n }\n\n const popover = openPopover;\n const chip = openChip;\n const hadFocus = popover.contains(popover.ownerDocument.activeElement);\n\n popover.hidden = true;\n chip?.setAttribute('aria-expanded', 'false');\n openPopover = null;\n openChip = null;\n\n if (restoreFocus && hadFocus) {\n chip?.focus();\n }\n};\n\n/**\n * Open the popover that belongs to a chip. Returns the popover, or null when\n * the chip has none.\n */\nconst open = (chip) => {\n close();\n\n const popover = popoverFor(chip);\n if (!popover) {\n return null;\n }\n\n popover.hidden = false;\n chip.setAttribute('aria-expanded', 'true');\n openPopover = popover;\n openChip = chip;\n\n position(chip, popover);\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => openPopover === popover && position(chip, popover));\n }\n\n return popover;\n};\n\nconst getOpen = () => openPopover;\n\nconst getOpenChip = () => openChip;\n\nexport { open, close, getOpen, getOpenChip, position, popoverFor };\n", "/**\n * Mark-for-removal: toggles a hidden input carrying the file id so the\n * server can delete the file when the form is saved.\n */\n\nconst DEFAULT_MARKED = 'Marked';\nconst DEFAULT_UNMARKED = 'Remove on save';\n\n/**\n * The element hidden inputs go into: the [data-file-chips-bucket] of the\n * chip's [data-file-chips] container, otherwise the chip's form.\n */\nconst targetFor = (chip) => chip.closest('[data-file-chips]')?.querySelector('[data-file-chips-bucket]') ?? chip.closest('form');\n\n// Compare in JS instead of building a CSS selector, so ids and input\n// names containing quotes or brackets cannot break the lookup.\nconst findInput = (target, name, id) => [...target.querySelectorAll('input[type=\"hidden\"]')]\n .find((input) => input.name === name && input.value === id) ?? null;\n\nconst render = (chip, button, marked) => {\n chip.classList.toggle('fc-chip--marked', marked);\n button.classList.toggle('fc-btn--active', marked);\n button.setAttribute('aria-pressed', String(marked));\n\n const label = button.querySelector('[data-file-chip-remove-label]') ?? button;\n label.textContent = marked\n ? (button.dataset.labelMarked ?? DEFAULT_MARKED)\n : (button.dataset.labelUnmarked ?? DEFAULT_UNMARKED);\n};\n\nconst buttonIn = (popover) => popover.querySelector('[data-file-chip-remove]');\n\n/**\n * Reflect the current mark state of a chip in its popover button.\n */\nconst sync = (chip, popover) => {\n const button = buttonIn(popover);\n const target = targetFor(chip);\n if (!button || !target) {\n return;\n }\n\n render(chip, button, findInput(target, button.dataset.fileChipInput, button.dataset.fileChipId) !== null);\n};\n\n/**\n * Toggle the mark of the chip whose popover holds this button.\n */\nconst toggle = (chip, button) => {\n const target = targetFor(chip);\n if (!target) {\n console.warn('[FileChips] No [data-file-chips-bucket] or <form> found for this chip; cannot mark it for removal.');\n return;\n }\n\n const name = button.dataset.fileChipInput;\n const id = button.dataset.fileChipId;\n const existing = findInput(target, name, id);\n\n if (existing) {\n existing.remove();\n } else {\n const input = chip.ownerDocument.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = id;\n target.appendChild(input);\n }\n\n const marked = !existing;\n render(chip, button, marked);\n chip.dispatchEvent(new CustomEvent('file-chips:change', { bubbles: true, detail: { id, input: name, marked } }));\n};\n\nexport { sync, toggle };\n", "/**\n * File Chips\n *\n * Compact file chips with a preview popover and mark-for-removal.\n * Markup contract (see README):\n * [data-file-chips] container\n * [data-file-chips-bucket] optional element that receives hidden inputs\n * [data-file-chip=\"<id>\"] chip, points at its popover by id\n * [data-file-chip-popover] popover panel (starts hidden)\n * [data-file-chip-remove] toggle button with data-file-chip-id and data-file-chip-input\n */\n\nimport { close, getOpen, getOpenChip, open, position } from './popover.js';\nimport { sync, toggle } from './marks.js';\n\n// One set of document listeners per document, shared by every root on it,\n// so several roots never handle the same event twice.\nconst documents = new Map();\n\nconst handled = (doc, element) => [...(documents.get(doc)?.roots ?? [])].some((root) => root.contains(element));\n\nconst openWithState = (chip) => {\n const popover = open(chip);\n if (popover) {\n sync(chip, popover);\n }\n\n return popover;\n};\n\nconst listenersFor = (doc) => ({\n click(event) {\n const target = event.target;\n\n const remove = target.closest?.('[data-file-chip-remove]');\n if (remove && getOpen()?.contains(remove)) {\n toggle(getOpenChip(), remove);\n return;\n }\n\n const chip = target.closest?.('[data-file-chip]');\n if (!chip || !handled(doc, chip)) {\n return;\n }\n\n event.preventDefault();\n\n if (getOpenChip() === chip) {\n close();\n } else {\n openWithState(chip);\n }\n },\n\n mousedown(event) {\n const popover = getOpen();\n if (popover && !popover.contains(event.target) && !event.target.closest?.('[data-file-chip]')) {\n close();\n }\n },\n\n keydown(event) {\n if (event.key === 'Escape' && getOpen()) {\n close({ restoreFocus: true });\n }\n },\n\n reposition() {\n const popover = getOpen();\n if (popover) {\n position(getOpenChip(), popover);\n }\n },\n});\n\nconst listen = (doc, listeners, method) => {\n doc[method]('click', listeners.click);\n doc[method]('mousedown', listeners.mousedown);\n doc[method]('keydown', listeners.keydown);\n doc.defaultView?.[method]('resize', listeners.reposition);\n doc.defaultView?.[method]('scroll', listeners.reposition, true);\n};\n\n/**\n * Start handling chips inside `root` (default: the whole document). A root\n * that is already handled, itself or through an ancestor, is a no-op.\n * Returns a function that stops handling this root.\n */\nconst init = ({ root = document } = {}) => {\n const doc = root.ownerDocument ?? root;\n\n if (handled(doc, root)) {\n return () => {};\n }\n\n if (!documents.has(doc)) {\n const listeners = listenersFor(doc);\n documents.set(doc, { roots: new Set(), listeners });\n listen(doc, listeners, 'addEventListener');\n }\n\n const entry = documents.get(doc);\n entry.roots.add(root);\n\n return () => {\n if (!entry.roots.delete(root)) {\n return;\n }\n\n if (getOpenChip() && root.contains(getOpenChip())) {\n close();\n }\n\n if (entry.roots.size === 0) {\n listen(doc, entry.listeners, 'removeEventListener');\n documents.delete(doc);\n }\n };\n};\n\nconst FileChips = {\n init,\n open: openWithState,\n close: () => close(),\n getOpen,\n position,\n};\n\nexport { init, close, getOpen, position };\nexport default FileChips;\n", "/**\n * Script-tag entry: exposes window.FileChips and starts it on the whole\n * document once the DOM is ready. Add data-manual to the <script> tag to\n * skip the automatic start and call FileChips.init() yourself.\n */\n\nimport FileChips from './index.js';\n\nwindow.FileChips = FileChips;\n\nconst script = document.currentScript;\n\nif (!script?.hasAttribute('data-manual')) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => FileChips.init(), { once: true });\n } else {\n FileChips.init();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["openPopover", "openChip", "clamp", "value", "min", "max", "popoverFor", "chip", "position", "popover", "rect", "width", "height", "top", "left", "above", "origin", "close", "restoreFocus", "hadFocus", "open", "getOpen", "getOpenChip", "DEFAULT_MARKED", "DEFAULT_UNMARKED", "targetFor", "chip", "findInput", "target", "name", "id", "input", "render", "button", "marked", "label", "buttonIn", "popover", "sync", "toggle", "existing", "documents", "handled", "doc", "element", "root", "openWithState", "chip", "popover", "open", "sync", "listenersFor", "event", "target", "remove", "getOpen", "toggle", "getOpenChip", "close", "position", "listen", "listeners", "method", "init", "entry", "FileChips", "index_default", "FileChips", "index_default", "script"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,74 @@
1
1
  {
2
- "name": "@ozankurt/file-chips",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@ozankurt/file-chips",
3
+ "version": "1.1.0",
4
+ "description": "Compact file chips with a preview popover and mark-for-removal, framework-free",
5
+ "keywords": [
6
+ "file",
7
+ "attachments",
8
+ "chips",
9
+ "popover",
10
+ "upload",
11
+ "ui"
12
+ ],
13
+ "license": "MIT",
14
+ "author": {
15
+ "name": "Ozan Kurt",
16
+ "email": "me@ozankurt.com",
17
+ "url": "https://ozankurt.com"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/OzanKurt/file-chips.git"
22
+ },
23
+ "homepage": "https://github.com/OzanKurt/file-chips#readme",
24
+ "bugs": "https://github.com/OzanKurt/file-chips/issues",
25
+ "type": "module",
26
+ "main": "dist/file-chips.cjs",
27
+ "module": "dist/esm/file-chips.mjs",
28
+ "types": "dist/file-chips.d.ts",
29
+ "unpkg": "dist/file-chips.min.js",
30
+ "jsdelivr": "dist/file-chips.min.js",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/file-chips.d.ts",
34
+ "import": "./dist/esm/file-chips.mjs",
35
+ "require": "./dist/file-chips.cjs",
36
+ "default": "./dist/esm/file-chips.mjs"
37
+ },
38
+ "./style.css": "./dist/file-chips.css",
39
+ "./dist/*": "./dist/*",
40
+ "./package.json": "./package.json"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "CHANGELOG.md"
45
+ ],
46
+ "sideEffects": [
47
+ "*.css",
48
+ "./dist/file-chips.js",
49
+ "./dist/file-chips.min.js",
50
+ "./js/browser.js"
51
+ ],
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "scripts": {
56
+ "build": "node ./scripts/build.js",
57
+ "test": "vitest run",
58
+ "prepublishOnly": "npm test && npm run typecheck && npm run build",
59
+ "examples": "vite --config vite.examples.config.js",
60
+ "screenshots": "node scripts/screenshots.js",
61
+ "typecheck": "tsc -p tsconfig.types.json"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "devDependencies": {
67
+ "@playwright/test": "1.63.0",
68
+ "esbuild": "^0.28.2",
69
+ "happy-dom": "^21.0.3",
70
+ "typescript": "^5.9.3",
71
+ "vite": "8.3.4",
72
+ "vitest": "^5.0.5"
73
+ }
74
+ }