@unicitylabs/sphere-ui 0.1.27 → 0.1.28

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.
package/dist/index.d.ts CHANGED
@@ -314,9 +314,6 @@ interface MediaUploaderProps {
314
314
  * locally, render a blob-URL preview, and call onFileSelected(file). The
315
315
  * consumer is responsible for uploading later (e.g. after creating the
316
316
  * parent entity to get a real ownerId).
317
- *
318
- * In this mode the URL paste fallback is hidden — the parent entity does
319
- * not yet exist, so an external URL can't be persisted to it anyway.
320
317
  */
321
318
  deferUpload?: boolean;
322
319
  onFileSelected?: (file: File | null) => void;
package/dist/index.js CHANGED
@@ -1478,6 +1478,7 @@ function MediaUploader({
1478
1478
  const limit = MEDIA_LIMITS[kind];
1479
1479
  const [state, setState] = useState7({ phase: "idle" });
1480
1480
  const [urlInput, setUrlInput] = useState7(value && !value.startsWith("blob:") ? value : "");
1481
+ const [source, setSource] = useState7("upload");
1481
1482
  const previewRef = useRef3(null);
1482
1483
  const fileInputRef = useRef3(null);
1483
1484
  const isPlaceholder = value?.includes("placehold.co") ?? false;
@@ -1506,6 +1507,7 @@ function MediaUploader({
1506
1507
  });
1507
1508
  return;
1508
1509
  }
1510
+ setUrlInput("");
1509
1511
  const fitted = await fitImage(file, limit);
1510
1512
  if (fitted.type !== "image/svg+xml") {
1511
1513
  const size = await readImageSize(fitted);
@@ -1609,9 +1611,26 @@ function MediaUploader({
1609
1611
  setUrlInput("");
1610
1612
  if (deferUpload) onFileSelected?.(null);
1611
1613
  };
1614
+ const commitUrl = () => {
1615
+ const url = urlInput.trim();
1616
+ onChange(url || null);
1617
+ if (url) {
1618
+ if (previewRef.current) {
1619
+ URL.revokeObjectURL(previewRef.current);
1620
+ previewRef.current = null;
1621
+ }
1622
+ setState({ phase: "idle" });
1623
+ if (deferUpload) onFileSelected?.(null);
1624
+ }
1625
+ };
1626
+ const tabClass = (active) => `px-3 py-1 rounded-md transition-colors ${active ? "bg-white dark:bg-white/10 text-neutral-900 dark:text-white shadow-sm" : "text-neutral-500 dark:text-white/45 hover:text-neutral-700 dark:hover:text-white/70"}`;
1612
1627
  return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
1613
1628
  label && /* @__PURE__ */ jsx23("div", { className: "text-sm text-neutral-700 dark:text-white/70", children: label }),
1614
- /* @__PURE__ */ jsxs18(
1629
+ /* @__PURE__ */ jsxs18("div", { className: "inline-flex gap-0.5 rounded-lg p-0.5 bg-neutral-100 dark:bg-white/5 text-xs w-fit", children: [
1630
+ /* @__PURE__ */ jsx23("button", { type: "button", onClick: () => setSource("upload"), className: tabClass(source === "upload"), children: "Upload" }),
1631
+ /* @__PURE__ */ jsx23("button", { type: "button", onClick: () => setSource("url"), className: tabClass(source === "url"), children: "URL" })
1632
+ ] }),
1633
+ source === "upload" && /* @__PURE__ */ jsxs18(
1615
1634
  "div",
1616
1635
  {
1617
1636
  ...getRootProps(),
@@ -1717,19 +1736,16 @@ function MediaUploader({
1717
1736
  ]
1718
1737
  }
1719
1738
  ),
1720
- !deferUpload && /* @__PURE__ */ jsxs18(Fragment4, { children: [
1721
- /* @__PURE__ */ jsx23("div", { className: "text-xs text-neutral-500 dark:text-white/45", children: "or paste URL:" }),
1722
- /* @__PURE__ */ jsx23(
1723
- Input,
1724
- {
1725
- type: "url",
1726
- placeholder: "https://...",
1727
- value: urlInput,
1728
- onChange: (e) => setUrlInput(e.target.value),
1729
- onBlur: () => onChange(urlInput.trim() || null)
1730
- }
1731
- )
1732
- ] })
1739
+ source === "url" && /* @__PURE__ */ jsx23(
1740
+ Input,
1741
+ {
1742
+ type: "url",
1743
+ placeholder: "https://...",
1744
+ value: urlInput,
1745
+ onChange: (e) => setUrlInput(e.target.value),
1746
+ onBlur: commitUrl
1747
+ }
1748
+ )
1733
1749
  ] });
1734
1750
  }
1735
1751
 
@@ -1784,18 +1800,18 @@ function MediaGallery({ ownerType, ownerId, items, onChange, uploadFn, max = 10,
1784
1800
  };
1785
1801
  function handleDragEnd(e) {
1786
1802
  if (!e.over || e.active.id === e.over.id) return;
1787
- if (deferUpload) {
1788
- const oldIndex = pending.findIndex((p) => p.preview === e.active.id);
1789
- const newIndex = pending.findIndex((p) => p.preview === e.over.id);
1790
- emitPending(arrayMove(pending, oldIndex, newIndex));
1791
- } else {
1803
+ if (items.some((i) => i.url === e.active.id)) {
1792
1804
  const oldIndex = items.findIndex((i) => i.url === e.active.id);
1793
1805
  const newIndex = items.findIndex((i) => i.url === e.over.id);
1794
- onChange(arrayMove(items, oldIndex, newIndex));
1806
+ if (newIndex >= 0) onChange(arrayMove(items, oldIndex, newIndex));
1807
+ } else if (deferUpload) {
1808
+ const oldIndex = pending.findIndex((p) => p.preview === e.active.id);
1809
+ const newIndex = pending.findIndex((p) => p.preview === e.over.id);
1810
+ if (newIndex >= 0) emitPending(arrayMove(pending, oldIndex, newIndex));
1795
1811
  }
1796
1812
  }
1797
- const count = deferUpload ? pending.length : items.length;
1798
- const tileIds = deferUpload ? pending.map((p) => p.preview) : items.map((i) => i.url);
1813
+ const count = items.length + (deferUpload ? pending.length : 0);
1814
+ const tileIds = [...items.map((i) => i.url), ...deferUpload ? pending.map((p) => p.preview) : []];
1799
1815
  return /* @__PURE__ */ jsxs19(
1800
1816
  "div",
1801
1817
  {
@@ -1811,7 +1827,15 @@ function MediaGallery({ ownerType, ownerId, items, onChange, uploadFn, max = 10,
1811
1827
  ")"
1812
1828
  ] }),
1813
1829
  /* @__PURE__ */ jsx24(DndContext, { collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx24(SortableContext, { items: tileIds, strategy: horizontalListSortingStrategy, children: /* @__PURE__ */ jsxs19("div", { className: "flex flex-wrap gap-2", children: [
1814
- deferUpload ? pending.map((p, i) => /* @__PURE__ */ jsx24(
1830
+ items.map((item, i) => /* @__PURE__ */ jsx24(
1831
+ SortableTile,
1832
+ {
1833
+ item,
1834
+ onRemove: () => onChange(items.filter((_, j) => j !== i))
1835
+ },
1836
+ item.url
1837
+ )),
1838
+ deferUpload && pending.map((p, i) => /* @__PURE__ */ jsx24(
1815
1839
  SortableTile,
1816
1840
  {
1817
1841
  item: { type: "screenshot", url: p.preview },
@@ -1821,13 +1845,6 @@ function MediaGallery({ ownerType, ownerId, items, onChange, uploadFn, max = 10,
1821
1845
  }
1822
1846
  },
1823
1847
  p.preview
1824
- )) : items.map((item, i) => /* @__PURE__ */ jsx24(
1825
- SortableTile,
1826
- {
1827
- item,
1828
- onRemove: () => onChange(items.filter((_, j) => j !== i))
1829
- },
1830
- item.url
1831
1848
  )),
1832
1849
  count < max && !adding && /* @__PURE__ */ jsx24(
1833
1850
  "button",
@@ -1856,13 +1873,9 @@ function MediaGallery({ ownerType, ownerId, items, onChange, uploadFn, max = 10,
1856
1873
  }
1857
1874
  setAdding(false);
1858
1875
  } : void 0,
1859
- onChange: deferUpload ? () => {
1860
- } : (url) => {
1861
- if (url) {
1862
- const isDuplicate = items.some((i) => i.url === url);
1863
- if (!isDuplicate) {
1864
- onChange([...items, { type: "screenshot", url }]);
1865
- }
1876
+ onChange: (url) => {
1877
+ if (url && !items.some((i) => i.url === url)) {
1878
+ onChange([...items, { type: "screenshot", url }]);
1866
1879
  }
1867
1880
  setAdding(false);
1868
1881
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unicitylabs/sphere-ui",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",