@ibanzajoe/uploader 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -64,6 +64,57 @@ Inline drop zone that can be embedded in a form.
64
64
 
65
65
  Headless hook — returns `{ open, uploading, files, errors }`.
66
66
 
67
+ ## Theming
68
+
69
+ The picker is styled entirely from `--uploader-*` CSS custom properties, so a
70
+ third-party app can restyle it to match its own brand. There are two ways to do
71
+ it — pick whichever fits your stack.
72
+
73
+ ### 1. The `theme` prop (scoped, per-instance)
74
+
75
+ Pass your design tokens as a plain object. They are applied as inline CSS
76
+ variables on that component's root, so the override is **scoped to that
77
+ instance** — two pickers on one page can carry different themes and nothing
78
+ leaks to the rest of your app. Only the keys you set change; everything else
79
+ falls back to the shipped defaults.
80
+
81
+ ```tsx
82
+ import { PickerOverlay } from '@uploader/react'
83
+ import '@uploader/react/styles.css'
84
+
85
+ <PickerOverlay
86
+ apikey="pk_…"
87
+ open={open}
88
+ onClose={() => setOpen(false)}
89
+ theme={{
90
+ accent: '#4f46e5', // your brand color
91
+ accentSoft: '#eef2ff', // tinted hover/icon background
92
+ accentRing: 'rgba(79,70,229,.28)',
93
+ radius: '12px',
94
+ font: "'Inter', sans-serif",
95
+ }}
96
+ />
97
+ ```
98
+
99
+ `DropPane` takes the same `theme` prop. See the `UploaderTheme` type for the
100
+ full token list (accent set, surfaces, text, borders, semantic colors, shadows,
101
+ radius, font, transition). A `themeToVars(theme)` helper is also exported if you
102
+ want to apply the same variables to your own wrapper element.
103
+
104
+ ### 2. CSS variables (global / page-level)
105
+
106
+ Override the variables in your own stylesheet to theme every instance at once:
107
+
108
+ ```css
109
+ :root {
110
+ --uploader-accent: #4f46e5;
111
+ --uploader-accent-soft: #eef2ff;
112
+ --uploader-radius: 12px;
113
+ }
114
+ ```
115
+
116
+ Dark mode is handled automatically when any ancestor has `data-theme="dark"`.
117
+
67
118
  ## Camera capture
68
119
 
69
120
  Both `<PickerOverlay>` and `<DropPane>` can capture a photo straight from the
package/dist/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(src_exports, {
35
35
  resize: () => resize,
36
36
  rotate: () => rotate,
37
37
  shouldOfferCamera: () => shouldOfferCamera,
38
+ themeToVars: () => themeToVars,
38
39
  transformUrl: () => transformUrl,
39
40
  usePicker: () => usePicker
40
41
  });
@@ -1405,6 +1406,43 @@ function FileQueueRow({ entry, onRemove, onRetry, onEdit }) {
1405
1406
  ] });
1406
1407
  }
1407
1408
 
1409
+ // src/react/theme.ts
1410
+ var VAR_MAP = {
1411
+ accent: "--uploader-accent",
1412
+ accentHover: "--uploader-accent-hover",
1413
+ accentSoft: "--uploader-accent-soft",
1414
+ accentRing: "--uploader-accent-ring",
1415
+ radius: "--uploader-radius",
1416
+ background: "--uploader-bg",
1417
+ surface: "--uploader-surface",
1418
+ surfaceHover: "--uploader-surface-hover",
1419
+ border: "--uploader-border",
1420
+ borderSubtle: "--uploader-border-subtle",
1421
+ text: "--uploader-text",
1422
+ textMuted: "--uploader-text-muted",
1423
+ textXMuted: "--uploader-text-xmuted",
1424
+ error: "--uploader-error",
1425
+ errorSoft: "--uploader-error-soft",
1426
+ success: "--uploader-success",
1427
+ shadowSm: "--uploader-shadow-sm",
1428
+ shadow: "--uploader-shadow",
1429
+ shadowLg: "--uploader-shadow-lg",
1430
+ font: "--uploader-font",
1431
+ transition: "--uploader-transition",
1432
+ zBackdrop: "--uploader-z-backdrop",
1433
+ zPanel: "--uploader-z-panel"
1434
+ };
1435
+ function themeToVars(theme) {
1436
+ if (!theme) return {};
1437
+ const vars = {};
1438
+ for (const key of Object.keys(theme)) {
1439
+ const value = theme[key];
1440
+ if (value === void 0 || value === null) continue;
1441
+ vars[VAR_MAP[key]] = String(value);
1442
+ }
1443
+ return vars;
1444
+ }
1445
+
1408
1446
  // src/react/PickerOverlay.tsx
1409
1447
  var import_jsx_runtime6 = require("react/jsx-runtime");
1410
1448
  function dialogTitle(isUploading, isDone, totalFiles, doneCount, errorCount, editingId, cameraOpen) {
@@ -1426,6 +1464,7 @@ function PickerOverlay({
1426
1464
  onFileUploadFinished,
1427
1465
  onFileUploadFailed,
1428
1466
  onCancel,
1467
+ theme,
1429
1468
  className,
1430
1469
  style
1431
1470
  }) {
@@ -1539,6 +1578,7 @@ function PickerOverlay({
1539
1578
  {
1540
1579
  className: "uploader-backdrop",
1541
1580
  "data-testid": "picker-backdrop",
1581
+ style: themeToVars(theme),
1542
1582
  onClick: handleClose,
1543
1583
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1544
1584
  "div",
@@ -1725,11 +1765,13 @@ function DropPane({
1725
1765
  onFileUploadFailed,
1726
1766
  onCancel,
1727
1767
  hint = "or click to browse \xB7 max 50 MB",
1768
+ theme,
1728
1769
  className,
1729
1770
  style
1730
1771
  }) {
1731
1772
  const [isDragOver, setIsDragOver] = (0, import_react7.useState)(false);
1732
1773
  const [cameraOpen, setCameraOpen] = (0, import_react7.useState)(false);
1774
+ const rootStyle = { ...themeToVars(theme), ...style };
1733
1775
  const { files, addFiles, removeFile, retryFile, upload, progress, isUploading, isDone } = usePicker({
1734
1776
  apikey,
1735
1777
  apiUrl,
@@ -1775,7 +1817,7 @@ function DropPane({
1775
1817
  {
1776
1818
  className: ["uploader-drop-pane", className ?? ""].filter(Boolean).join(" "),
1777
1819
  "data-testid": "drop-pane",
1778
- style,
1820
+ style: rootStyle,
1779
1821
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1780
1822
  CameraCapture,
1781
1823
  {
@@ -1796,7 +1838,7 @@ function DropPane({
1796
1838
  className: ["uploader-drop-pane", className ?? ""].filter(Boolean).join(" "),
1797
1839
  "data-drag-over": isDragOver ? "true" : void 0,
1798
1840
  "data-testid": "drop-pane",
1799
- style,
1841
+ style: rootStyle,
1800
1842
  children: [
1801
1843
  files.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1802
1844
  Dropzone,
@@ -1908,6 +1950,7 @@ function DropPane({
1908
1950
  resize,
1909
1951
  rotate,
1910
1952
  shouldOfferCamera,
1953
+ themeToVars,
1911
1954
  transformUrl,
1912
1955
  usePicker
1913
1956
  });