@12-apps/storage 2.2.0 → 2.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/storage",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "description": "Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the local-disk driver, the image-pipeline port, the orphan reclaim), Hono adapter (./hono), the S3-compatible driver behind its own subpath (./s3) and the bound picker + upload hook (./react). Standardized adoption contract in ADOPTING.md.",
6
6
  "exports": {
@@ -43,7 +43,7 @@
43
43
  }
44
44
  },
45
45
  "dependencies": {
46
- "@12-apps/ui": "^6.3.1",
46
+ "@12-apps/ui": "^6.8.0",
47
47
  "zod": "^4.3.5"
48
48
  },
49
49
  "devDependencies": {
@@ -85,6 +85,8 @@ export function createWebStorage(config: WebStorageConfig): WebStorage {
85
85
  value={imageKey}
86
86
  onChange={setImageKey}
87
87
  label={messages.fieldLabel}
88
+ removeLabel={messages.fieldRemove}
89
+ uploadCopy={messages.field}
88
90
  helperText={messages.fieldHelper}
89
91
  />
90
92
  </Stack>
@@ -1,3 +1,5 @@
1
+ import type { UploadButtonCopy } from '@12-apps/ui/copy';
2
+
1
3
  import { ACCEPTED_CONTENT_TYPES } from '../content-types';
2
4
  import { megabytes } from '../limits';
3
5
 
@@ -100,13 +102,24 @@ export interface WebStorageMessages {
100
102
  /**
101
103
  * The standalone upload page's own chrome — heading, the one-line
102
104
  * instruction (which carries the ceiling, same rule as the refusals), the
103
- * field label and its helper. These rendered as literals inside
104
- * `createWebStorage` until copy became required config.
105
+ * field label, its helper, and the button that clears a chosen image.
106
+ * These rendered as literals inside `createWebStorage` until copy became
107
+ * required config.
105
108
  */
106
109
  pageTitle: string;
107
110
  pageIntro: string;
108
111
  fieldLabel: string;
109
112
  fieldHelper: string;
113
+ fieldRemove: string;
114
+ /**
115
+ * What the dropzone ITSELF says — its hint line, its accessible name, and
116
+ * the three announcements only a screen reader receives. `@12-apps/ui` stopped
117
+ * shipping English defaults for these, so a host that answers `fieldLabel`
118
+ * and nothing else would have a dropzone with no name and a silent progress
119
+ * bar. `buttonLabel` is unused here: `ImageField` passes `fieldLabel` as the
120
+ * override, because the field's own label is the one a form already names.
121
+ */
122
+ field: UploadButtonCopy;
110
123
  }
111
124
 
112
125
  /** Copy that only makes sense with the configured ceiling in it. */
@@ -5,6 +5,7 @@ import { Box } from '@12-apps/ui/mui/Box';
5
5
  import { Stack } from '@12-apps/ui/mui/Stack';
6
6
  import { Text } from '@12-apps/ui/typography/Text';
7
7
  import { Button } from '@12-apps/ui/form/Button';
8
+ import type { UploadButtonCopy } from '@12-apps/ui/copy';
8
9
  import { UploadButton } from '@12-apps/ui/form/UploadButton';
9
10
 
10
11
  import { ACCEPTED_CONTENT_TYPES } from '../content-types';
@@ -34,7 +35,17 @@ export interface ImageFieldProps {
34
35
  previewUrl?: string | null;
35
36
  /** Called with the new key, or `null` when the image is removed. */
36
37
  onChange: (imageKey: string | null) => void;
37
- label?: string;
38
+ /** The dropzone's own label. REQUIRED — this package ships no default copy. */
39
+ label: string;
40
+ /** The button that clears a chosen image. REQUIRED, for the same reason. */
41
+ removeLabel: string;
42
+ /**
43
+ * What the dropzone itself says — the hint under the label, its accessible
44
+ * name, and the three announcements only a screen reader receives. REQUIRED:
45
+ * `@12-apps/ui` ships no default copy, so without this the dropzone has no
46
+ * name and a silent progress bar.
47
+ */
48
+ uploadCopy: UploadButtonCopy;
38
49
  helperText?: string;
39
50
  /** Passed through per upload — `{ optimize: false }` for a rendered canvas. */
40
51
  uploadOptions?: UploadOptions;
@@ -89,7 +100,10 @@ export function ImageField(props: BoundImageFieldProps): JSX.Element {
89
100
  ) : null}
90
101
  <UploadButton
91
102
  variant="dropzone"
92
- label={props.label ?? 'Enviar imagem'}
103
+ copy={props.uploadCopy}
104
+ // The field's own label wins over `uploadCopy.buttonLabel`: a form
105
+ // already names this field, and naming it twice is how the two drift.
106
+ label={props.label}
93
107
  accept={ACCEPT}
94
108
  disabled={props.disabled || uploader.uploading}
95
109
  uploading={uploader.uploading}
@@ -100,7 +114,7 @@ export function ImageField(props: BoundImageFieldProps): JSX.Element {
100
114
  {preview ? (
101
115
  <Box>
102
116
  <Button variant="text" onClick={handleRemove} dataTestId={`${testId}-remove`}>
103
- Remover imagem
117
+ {props.removeLabel}
104
118
  </Button>
105
119
  </Box>
106
120
  ) : null}
@@ -34,5 +34,19 @@ export function PT_BR_WEB_STORAGE_MESSAGES(
34
34
  pageIntro: `Envie um PNG, JPG, WebP ou GIF de até ${context.limit}.`,
35
35
  fieldLabel: 'Foto do produto',
36
36
  fieldHelper: 'A imagem é reduzida no navegador antes do envio.',
37
+ fieldRemove: 'Remover imagem',
38
+ field: {
39
+ // `buttonLabel` is never read here — `ImageField` overrides it with
40
+ // `fieldLabel`. It is still answered, because the type is the whole of
41
+ // what `UploadButton` may render and a partial answer is not a pack.
42
+ buttonLabel: 'Foto do produto',
43
+ dropzoneHint: 'Solte a imagem aqui ou clique para escolher',
44
+ dropzoneRole: (label) => `Área de upload de imagem. ${label}`,
45
+ uploading: 'Enviando…',
46
+ dropReady: 'Imagem pronta para soltar',
47
+ percentUploaded: (percent) => `${percent}% enviado`,
48
+ uploadInProgress: (percent) => `Envio em andamento: ${percent}%`,
49
+ errorAnnouncement: (message) => `Erro: ${message}`,
50
+ },
37
51
  };
38
52
  }