@planningcenter/organization-avatars 1.6.3 → 1.8.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
@@ -2,15 +2,42 @@
2
2
 
3
3
  This package provides an `OrganizationAvatars` React component which encapsulates displaying and updating an organization's avatar.
4
4
 
5
- See [the component's type signature](https://github.com/planningcenter/organization-avatars/blob/main/src/types.ts) for up-to-date props.
6
-
7
5
  ## Usage
8
6
 
9
- If no `darkModeAvatarUrl` is provided, the `avatarUrl` will be rendered with dark styles in the dark mode avatar's UI slot.
7
+ ```tsx
8
+ import { OrganizationAvatars } from "@planningcenter/organization-avatars"
9
+ import "@planningcenter/organization-avatars/style.css"
10
+
11
+ <OrganizationAvatars
12
+ orgName="Demo Church"
13
+ avatarUrl="https://example.com/avatar.png"
14
+ darkModeAvatarUrl="https://example.com/avatar-dark.png"
15
+ showDarkModeAvatar
16
+ onAvatarUpdate={(mode, newUrl) => console.log(mode, newUrl)}
17
+ />
18
+ ```
19
+
20
+ To see a working example, see the [`/organization` page in Accounts](https://accounts.planningcenteronline.com/organization). The `OrganizationAvatars` component is mounted in [the `_church_information` partial](https://github.com/planningcenter/accounts/blob/main/app/views/organization/show/_church_information.html.erb).
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ | --- | --- | --- | --- |
26
+ | `orgName` | `string` | **required** | The organization name, used for avatar alt text. |
27
+ | `avatarUrl` | `string` | `undefined` | URL of the current light mode avatar. When no `darkModeAvatarUrl` is provided, this is also used for the dark mode slot. |
28
+ | `darkModeAvatarUrl` | `string` | `undefined` | URL of the current dark mode avatar. Falls back to `avatarUrl` if not set. |
29
+ | `showDarkModeAvatar` | `boolean` | `false` | Whether to render the dark mode avatar UI alongside the light mode avatar. Intended to be controlled by a feature flag. |
30
+ | `onAvatarUpdate` | `(mode: AvatarMode, newUrl: string) => void` | `undefined` | Callback fired after a successful upload or delete. `mode` is `"avatar"` or `"dark_mode_avatar"`. `newUrl` is the new URL (or `""` on delete). |
31
+ | `pcoEnv` | `Environment` | `undefined` | Planning Center environment override (`"production"`, `"staging"`, `"development"`, `"test"`, `"prototype"`). When omitted, the environment is inferred automatically. |
32
+ | `readOnly` | `boolean` | `false` | Disables the edit button, preventing uploads and deletes. |
33
+
34
+ ### Types
10
35
 
11
- The `showDarkModeAvatar` prop gates the rendering of the dark mode avatar UI. It is intended to be controlled by a feature flag, and will eventually be removed when dark mode avatars have been rolled out.
36
+ ```ts
37
+ type AvatarMode = "avatar" | "dark_mode_avatar"
12
38
 
13
- To see an example of how this package can be used, see the [`/organization` page in Accounts](https://accounts.planningcenteronline.com/organization). The `OrganizationAvatars` component is mounted in [the `_church_information` partial](https://github.com/planningcenter/accounts/blob/main/app/views/organization/show/_church_information.html.erb).
39
+ type Environment = "production" | "staging" | "development" | "test" | "prototype"
40
+ ```
14
41
 
15
42
  ## Development
16
43
 
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),i=require("react"),M=require("react-dom"),B=require("@planningcenter/url"),A=require("@planningcenter/tapestry"),x=require("@planningcenter/icons/paths/general"),O=require("@planningcenter/icons/paths/services"),P=require("react-dropzone");function z(a){var o,t,r="";if(typeof a=="string"||typeof a=="number")r+=a;else if(typeof a=="object")if(Array.isArray(a)){var l=a.length;for(o=0;o<l;o++)a[o]&&(t=z(a[o]))&&(r&&(r+=" "),r+=t)}else for(t in a)a[t]&&(r&&(r+=" "),r+=t);return r}function T(){for(var a,o,t=0,r="",l=arguments.length;t<l;t++)(a=arguments[t])&&(o=z(a))&&(r&&(r+=" "),r+=o);return r}function $(){const a=document.querySelector('meta[name="csrf-token"]');if(!a||!a.content)throw new Error('CSRF token not found. Ensure your Rails application includes <meta name="csrf-token" content="..."> in the document head.');return a.content}function L(a){const o=a.split("");return[o.shift()?.toUpperCase(),o.join("")].join("")}const I=a=>a==="dark_mode_avatar"?"dark":"light";function S({avatarUrl:a,organization:o,mode:t,pcoEnv:r,readOnly:l,onAvatarUpdate:j}){const[s,g]=i.useState(null),[c,_]=i.useState(!1),[d,f]=i.useState(!1),[b,u]=i.useState(""),p=i.useId(),v=i.useRef(null),y=I(t),F=h=>{const[n]=h;n&&g(Object.assign(n,{preview:URL.createObjectURL(n)}))};i.useEffect(()=>()=>{s?.preview&&(URL.revokeObjectURL(s.preview),u(""))},[s]);const N=async h=>{const n=await fetch(B.pcoApiUrl("accounts",{env:r}),{method:"PATCH",headers:{"Content-Type":"application/json","X-CSRF-Token":$(),Accept:"application/json"},body:JSON.stringify({data:{attributes:h}})}),m=await n.json();if(!n.ok)throw m;return m},R=async h=>{if(h.preventDefault(),!(!s||c||d)){_(!0);try{const n=new FileReader,m=await new Promise((w,k)=>{n.onloadend=()=>w(n.result),n.onerror=k,n.readAsDataURL(s)}),C=await N({[t]:m,[`${t}_cache`]:""});g(null),j(t,C.data.attributes[t].url),v.current?.close()}catch{u("We were unable to save your avatar.")}finally{_(!1)}}},U=async()=>{if(!(c||d)){f(!0);try{await N({[`remove_${t}`]:"1"}),j(t,""),v.current?.close()}catch{u("We were unable to delete your avatar.")}finally{f(!1)}}},q=()=>{g(null),u("")};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pco-org-avatar",children:[e.jsx("button",{type:"button",onClick:()=>v.current?.showModal(),disabled:l,className:`pco-org-avatar__button pco-org-avatar__button--${y}`,children:a?e.jsxs(e.Fragment,{children:[e.jsx("img",{src:a,alt:`Church logo for ${o}`}),e.jsx("div",{className:"tds-btn tds-btn--interaction tds-btn--icononly",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:x.pencil})})})]}):e.jsxs(e.Fragment,{children:[e.jsx("svg",{width:"48",height:"48",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:O.image})}),e.jsx("div",{className:"tds-btn tds-btn--interaction tds-btn--icononly",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:x.pencil})})})]})}),e.jsxs("div",{className:"pco-org-avatar__label",children:[L(y)," mode"]})]}),M.createPortal(e.jsx("dialog",{id:p,ref:v,className:"pco-org-avatar__dialog",onClose:q,children:e.jsxs("form",{onSubmit:R,children:[e.jsxs("div",{className:"pco-org-avatar__dialog-header",children:[e.jsxs("h1",{className:"pco-org-avatar__dialog-title",children:["Church logo — ",y," mode"]}),e.jsx("button",{type:"button",onClick:()=>v.current?.close(),className:"pco-org-avatar__dialog-close","aria-label":"Close modal",children:e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:x.smallX})})})]}),e.jsxs("div",{className:"pco-org-avatar__dialog-body",children:[b&&e.jsxs("div",{role:"alert",className:"pco-org-avatar__dialog-alert",children:[e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",className:"symbol","aria-hidden":"true",children:e.jsx("path",{d:x.exclamationTriangle})}),e.jsx("p",{children:b})]}),e.jsx(P,{accept:{"image/*":[".jpg",".jpeg",".png"]},multiple:!1,onDropAccepted:F,children:({getRootProps:h,getInputProps:n,isFocused:m,isDragAccept:C,isDragReject:w,isDragActive:k})=>{const D=T("pco-org-avatar__dialog-dropzone",`pco-org-avatar__dialog-dropzone--${y}`,{"pco-org-avatar__dialog-dropzone--image":s||a,"pco-org-avatar__dialog-dropzone--focused":m,"pco-org-avatar__dialog-dropzone--accepted":C,"pco-org-avatar__dialog-dropzone--rejected":k&&w});return e.jsxs("div",{...h({className:D}),children:[e.jsx("input",{...n()}),s?e.jsx("img",{src:s.preview,alt:`Church logo for ${o}`}):a?e.jsx("img",{src:a,alt:`Church logo for ${o}`}):e.jsxs(e.Fragment,{children:[e.jsx("svg",{width:"64",height:"64",viewBox:"0 0 16 16","aria-hidden":"true",children:e.jsx("path",{d:x.toCloudArrow})}),k&&w?e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:"Wrong file type"}),e.jsx("p",{children:"Please upload a PNG or JPG file."})]}):e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:"Add church logo"}),e.jsx("p",{children:"Use a high-quality image (up to 20mb) with a transparent background."})]})]})]})}})]}),e.jsxs("div",{className:"pco-org-avatar__dialog-actions",children:[a&&e.jsx(A.Button,{label:"Delete logo",kind:"delete",onClick:U,loading:d,disabled:d||c,className:"pco-org-avatar__dialog-delete"}),e.jsx(A.Button,{type:"button",label:"Cancel",onClick:()=>v.current?.close(),disabled:c||d}),e.jsx(A.Button,{type:"submit",label:"Update logo",kind:"primary",loading:c,disabled:!s||c||d})]})]})}),document.body)]})}function E({avatarUrl:a,darkModeAvatarUrl:o,orgName:t,onAvatarUpdate:r,pcoEnv:l,readOnly:j,showDarkModeAvatar:s=!1}){const[g,c]=i.useState(a),[_,d]=i.useState(o),f=(u,p)=>{u==="avatar"?(c(p),r?.("avatar",p)):u==="dark_mode_avatar"&&(d(p),r?.("dark_mode_avatar",p))},b=_||g;return e.jsxs("div",{className:"pco-org-avatars",children:[e.jsx(S,{avatarUrl:g,organization:t,mode:"avatar",pcoEnv:l,readOnly:j,onAvatarUpdate:f}),s&&e.jsx(S,{avatarUrl:b,organization:t,mode:"dark_mode_avatar",pcoEnv:l,readOnly:j,onAvatarUpdate:f})]})}exports.OrganizationAvatars=E;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),c=require("react"),P=require("react-dom"),$=require("@planningcenter/url"),y=require("@planningcenter/tapestry"),I=require("@planningcenter/sweetest-alert"),x=require("@planningcenter/icons/paths/general"),L=require("@planningcenter/icons/paths/services"),E=require("react-dropzone");function B(a){var r,t,o="";if(typeof a=="string"||typeof a=="number")o+=a;else if(typeof a=="object")if(Array.isArray(a)){var d=a.length;for(r=0;r<d;r++)a[r]&&(t=B(a[r]))&&(o&&(o+=" "),o+=t)}else for(t in a)a[t]&&(o&&(o+=" "),o+=t);return o}function W(){for(var a,r,t=0,o="",d=arguments.length;t<d;t++)(a=arguments[t])&&(r=B(a))&&(o&&(o+=" "),o+=r);return o}function G(){const a=document.querySelector('meta[name="csrf-token"]');if(!a||!a.content)throw new Error('CSRF token not found. Ensure your Rails application includes <meta name="csrf-token" content="..."> in the document head.');return a.content}function J(a){const r=a.split("");return[r.shift()?.toUpperCase(),r.join("")].join("")}const X=a=>a==="dark_mode_avatar"?"dark":"light",U=({open:a})=>e.jsxs("h2",{className:"pco-org-avatar__instructions",children:["Drag & drop your logo file to upload, or"," ",e.jsx(y.Button,{label:"browse",kind:"inline-text",onClick:r=>{r.stopPropagation(),a()}})]});function z({avatarUrl:a,organization:r,mode:t,pcoEnv:o,readOnly:d,onAvatarUpdate:f,fallbackAvatarUrl:b}){const[n,_]=c.useState(null),[l,w]=c.useState(!1),[i,p]=c.useState(!1),[g,k]=c.useState(""),q=c.useId(),m=c.useRef(null),u=X(t),v=a||b,T=h=>{const[s]=h;s&&_(Object.assign(s,{preview:URL.createObjectURL(s)}))};c.useEffect(()=>()=>{n?.preview&&(URL.revokeObjectURL(n.preview),k(""))},[n]);const R=async h=>{const s=await fetch($.pcoApiUrl("accounts",{env:o}),{method:"PATCH",headers:{"Content-Type":"application/json","X-CSRF-Token":G(),Accept:"application/json"},body:JSON.stringify({data:{attributes:h}})}),j=await s.json();if(!s.ok)throw j;return j},D=async h=>{if(h.preventDefault(),!(!n||l||i)){w(!0);try{const s=new FileReader,j=await new Promise((C,N)=>{s.onloadend=()=>C(s.result),s.onerror=N,s.readAsDataURL(n)}),A=await R({[t]:j,[`${t}_cache`]:""});_(null),f(t,A.data.attributes[t].url),m.current?.close()}catch{k("We were unable to save your avatar.")}finally{w(!1)}}},S=async()=>{l||i||(p(!0),I.SweetestAlert({type:"danger",title:"Remove this logo?",content:u==="light"?"Removing this logo means no logo will appear in light mode. This cannot be undone.":b?"Removing this logo means your light mode logo will appear in dark mode. This cannot be undone.":"Removing this logo means no logo will appear in dark mode. This cannot be undone.",confirmButton:"Remove logo",onConfirm:async()=>{try{await R({[`remove_${t}`]:"1"}),f(t,""),m.current?.close()}catch{k("We were unable to delete your avatar.")}finally{p(!1)}},onCancel:()=>{p(!1)}}))},M=()=>{_(null),k("")};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pco-org-avatar",children:[e.jsx("button",{type:"button",onClick:()=>m.current?.showModal(),disabled:d,className:`pco-org-avatar__button pco-org-avatar__button--${u}`,children:v?e.jsxs(e.Fragment,{children:[e.jsx("img",{src:v,alt:`Church logo for ${r}`}),e.jsx("div",{className:"tds-btn tds-btn--interaction tds-btn--icononly",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:x.pencil})})})]}):e.jsxs(e.Fragment,{children:[e.jsx("svg",{width:"48",height:"48",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:L.image})}),e.jsx("div",{className:"tds-btn tds-btn--interaction tds-btn--icononly",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:x.pencil})})})]})}),e.jsxs("div",{className:"pco-org-avatar__label",children:[J(u)," mode"]})]}),P.createPortal(e.jsx("dialog",{id:q,ref:m,className:"pco-org-avatar__dialog",onClose:M,children:e.jsxs("form",{onSubmit:D,children:[e.jsxs("div",{className:"pco-org-avatar__dialog-header",children:[e.jsxs("h1",{className:"pco-org-avatar__dialog-title",children:["Church logo — ",u," mode"]}),e.jsx("button",{type:"button",onClick:()=>m.current?.close(),className:"pco-org-avatar__dialog-close","aria-label":"Close modal",children:e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:x.smallX})})})]}),e.jsxs("div",{className:"pco-org-avatar__dialog-body",children:[g&&e.jsxs("div",{role:"alert",className:"pco-org-avatar-alert pco-org-avatar__dialog-alert",children:[e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",className:"symbol","aria-hidden":"true",children:e.jsx("path",{d:x.exclamationTriangle})}),e.jsx("p",{children:g})]}),e.jsx(E,{accept:{"image/*":[".jpg",".jpeg",".png"]},multiple:!1,onDropAccepted:T,children:({getRootProps:h,getInputProps:s,isFocused:j,isDragAccept:A,isDragReject:C,isDragActive:N,open:F})=>{const O=W("pco-org-avatar__dialog-dropzone",`pco-org-avatar__dialog-dropzone--${u}`,{"pco-org-avatar__dialog-dropzone--image":n||v,"pco-org-avatar__dialog-dropzone--focused":j,"pco-org-avatar__dialog-dropzone--accepted":A,"pco-org-avatar__dialog-dropzone--rejected":N&&C});return e.jsxs(e.Fragment,{children:[e.jsxs("div",{...h({className:O}),children:[e.jsx("input",{...s()}),n?e.jsx("img",{src:n.preview,alt:`Church logo for ${r}`}):v?e.jsx("img",{src:v,alt:`Church logo for ${r}`}):e.jsxs(e.Fragment,{children:[e.jsx("svg",{width:"64",height:"64",viewBox:"0 0 16 16","aria-hidden":"true",children:e.jsx("path",{d:x.toCloudArrow})}),N&&C?e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:"Wrong file type"}),e.jsx("p",{children:"Please upload a PNG or JPG file."})]}):e.jsxs(e.Fragment,{children:[e.jsx(U,{open:F}),e.jsx("p",{className:"pco-org-avatar__instructions-sub",children:"Use a high-quality image (up to 20mb) with a transparent background."})]})]})]}),(n||v)&&e.jsx(U,{open:F})]})}})]}),e.jsxs("div",{className:"pco-org-avatar__dialog-actions",children:[a&&u==="light"&&e.jsx(y.Button,{label:"Remove logo",kind:"secondary-delete",onClick:S,loading:i,disabled:i||l,className:"pco-org-avatar__dialog-delete"}),a&&u==="dark"&&e.jsx(y.Button,{label:"Use light mode logo",kind:"secondary",onClick:S,loading:i,disabled:i||l,className:"pco-org-avatar__dialog-delete"}),e.jsx(y.Button,{type:"button",label:"Cancel",onClick:()=>m.current?.close(),disabled:l||i}),e.jsx(y.Button,{type:"submit",label:"Update logo",kind:"primary",loading:l,disabled:!n||l||i})]})]})}),document.body)]})}function H({avatarUrl:a,darkModeAvatarUrl:r,orgName:t,onAvatarUpdate:o,pcoEnv:d,readOnly:f,showDarkModeAvatar:b=!1}){const[n,_]=c.useState(a),[l,w]=c.useState(r),i=(p,g)=>{p==="avatar"?(_(g),o?.("avatar",g)):p==="dark_mode_avatar"&&(w(g),o?.("dark_mode_avatar",g))};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pco-org-avatars",children:[e.jsx(z,{avatarUrl:n,organization:t,mode:"avatar",pcoEnv:d,readOnly:f,onAvatarUpdate:i}),b&&e.jsx(z,{avatarUrl:l,organization:t,mode:"dark_mode_avatar",pcoEnv:d,readOnly:f,onAvatarUpdate:i,fallbackAvatarUrl:n})]}),b&&n&&!l&&e.jsxs("div",{className:"pco-org-avatar-alert",children:[e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",className:"symbol","aria-hidden":"true",children:e.jsx("path",{d:x.infoCircle})}),e.jsx("p",{children:"The same logo is being used for both light and dark modes."})]})]})}exports.OrganizationAvatars=H;
package/dist/index.js CHANGED
@@ -1,25 +1,26 @@
1
- import { jsxs as n, Fragment as f, jsx as e } from "react/jsx-runtime";
2
- import { useState as _, useId as O, useRef as P, useEffect as T } from "react";
3
- import { createPortal as L } from "react-dom";
4
- import { pcoApiUrl as I } from "@planningcenter/url";
5
- import { Button as z } from "@planningcenter/tapestry";
6
- import { pencil as x, smallX as E, exclamationTriangle as W, toCloudArrow as q } from "@planningcenter/icons/paths/general";
7
- import { image as G } from "@planningcenter/icons/paths/services";
8
- import J from "react-dropzone";
9
- function M(a) {
10
- var o, r, t = "";
11
- if (typeof a == "string" || typeof a == "number") t += a;
1
+ import { jsxs as r, Fragment as h, jsx as e } from "react/jsx-runtime";
2
+ import { useState as _, useId as L, useRef as E, useEffect as W } from "react";
3
+ import { createPortal as q } from "react-dom";
4
+ import { pcoApiUrl as G } from "@planningcenter/url";
5
+ import { Button as C } from "@planningcenter/tapestry";
6
+ import { SweetestAlert as J } from "@planningcenter/sweetest-alert";
7
+ import { pencil as T, smallX as X, exclamationTriangle as H, toCloudArrow as K, infoCircle as Q } from "@planningcenter/icons/paths/general";
8
+ import { image as V } from "@planningcenter/icons/paths/services";
9
+ import Y from "react-dropzone";
10
+ function D(a) {
11
+ var t, o, n = "";
12
+ if (typeof a == "string" || typeof a == "number") n += a;
12
13
  else if (typeof a == "object") if (Array.isArray(a)) {
13
- var c = a.length;
14
- for (o = 0; o < c; o++) a[o] && (r = M(a[o])) && (t && (t += " "), t += r);
15
- } else for (r in a) a[r] && (t && (t += " "), t += r);
16
- return t;
14
+ var s = a.length;
15
+ for (t = 0; t < s; t++) a[t] && (o = D(a[t])) && (n && (n += " "), n += o);
16
+ } else for (o in a) a[o] && (n && (n += " "), n += o);
17
+ return n;
17
18
  }
18
- function X() {
19
- for (var a, o, r = 0, t = "", c = arguments.length; r < c; r++) (a = arguments[r]) && (o = M(a)) && (t && (t += " "), t += o);
20
- return t;
19
+ function Z() {
20
+ for (var a, t, o = 0, n = "", s = arguments.length; o < s; o++) (a = arguments[o]) && (t = D(a)) && (n && (n += " "), n += t);
21
+ return n;
21
22
  }
22
- function H() {
23
+ function ee() {
23
24
  const a = document.querySelector('meta[name="csrf-token"]');
24
25
  if (!a || !a.content)
25
26
  throw new Error(
@@ -27,36 +28,50 @@ function H() {
27
28
  );
28
29
  return a.content;
29
30
  }
30
- function K(a) {
31
- const o = a.split("");
32
- return [o.shift()?.toUpperCase(), o.join("")].join("");
31
+ function ae(a) {
32
+ const t = a.split("");
33
+ return [t.shift()?.toUpperCase(), t.join("")].join("");
33
34
  }
34
- const Q = (a) => a === "dark_mode_avatar" ? "dark" : "light";
35
- function D({
35
+ const oe = (a) => a === "dark_mode_avatar" ? "dark" : "light", B = ({ open: a }) => /* @__PURE__ */ r("h2", { className: "pco-org-avatar__instructions", children: [
36
+ "Drag & drop your logo file to upload, or",
37
+ " ",
38
+ /* @__PURE__ */ e(
39
+ C,
40
+ {
41
+ label: "browse",
42
+ kind: "inline-text",
43
+ onClick: (t) => {
44
+ t.stopPropagation(), a();
45
+ }
46
+ }
47
+ )
48
+ ] });
49
+ function F({
36
50
  avatarUrl: a,
37
- organization: o,
38
- mode: r,
39
- pcoEnv: t,
40
- readOnly: c,
41
- onAvatarUpdate: b
51
+ organization: t,
52
+ mode: o,
53
+ pcoEnv: n,
54
+ readOnly: s,
55
+ onAvatarUpdate: y,
56
+ fallbackAvatarUrl: w
42
57
  }) {
43
- const [l, p] = _(null), [d, w] = _(!1), [s, y] = _(!1), [N, h] = _(""), g = O(), m = P(null), k = Q(r), R = (u) => {
44
- const [i] = u;
45
- i && p(
46
- Object.assign(i, {
47
- preview: URL.createObjectURL(i)
58
+ const [i, k] = _(null), [d, N] = _(!1), [c, m] = _(!1), [g, A] = _(""), M = L(), v = E(null), p = oe(o), f = a || w, P = (u) => {
59
+ const [l] = u;
60
+ l && k(
61
+ Object.assign(l, {
62
+ preview: URL.createObjectURL(l)
48
63
  })
49
64
  );
50
65
  };
51
- T(() => () => {
52
- l?.preview && (URL.revokeObjectURL(l.preview), h(""));
53
- }, [l]);
54
- const U = async (u) => {
55
- const i = await fetch(I("accounts", { env: t }), {
66
+ W(() => () => {
67
+ i?.preview && (URL.revokeObjectURL(i.preview), A(""));
68
+ }, [i]);
69
+ const x = async (u) => {
70
+ const l = await fetch(G("accounts", { env: n }), {
56
71
  method: "PATCH",
57
72
  headers: {
58
73
  "Content-Type": "application/json",
59
- "X-CSRF-Token": H(),
74
+ "X-CSRF-Token": ee(),
60
75
  Accept: "application/json"
61
76
  },
62
77
  body: JSON.stringify({
@@ -64,53 +79,61 @@ function D({
64
79
  attributes: u
65
80
  }
66
81
  })
67
- }), v = await i.json();
68
- if (!i.ok) throw v;
69
- return v;
70
- }, S = async (u) => {
71
- if (u.preventDefault(), !(!l || d || s)) {
72
- w(!0);
82
+ }), b = await l.json();
83
+ if (!l.ok) throw b;
84
+ return b;
85
+ }, $ = async (u) => {
86
+ if (u.preventDefault(), !(!i || d || c)) {
87
+ N(!0);
73
88
  try {
74
- const i = new FileReader(), v = await new Promise((C, A) => {
75
- i.onloadend = () => C(i.result), i.onerror = A, i.readAsDataURL(l);
76
- }), j = await U({
77
- [r]: v,
78
- [`${r}_cache`]: ""
89
+ const l = new FileReader(), b = await new Promise((R, U) => {
90
+ l.onloadend = () => R(l.result), l.onerror = U, l.readAsDataURL(i);
91
+ }), j = await x({
92
+ [o]: b,
93
+ [`${o}_cache`]: ""
79
94
  });
80
- p(null), b(r, j.data.attributes[r].url), m.current?.close();
95
+ k(null), y(o, j.data.attributes[o].url), v.current?.close();
81
96
  } catch {
82
- h("We were unable to save your avatar.");
97
+ A("We were unable to save your avatar.");
83
98
  } finally {
84
- w(!1);
99
+ N(!1);
85
100
  }
86
101
  }
87
- }, F = async () => {
88
- if (!(d || s)) {
89
- y(!0);
90
- try {
91
- await U({
92
- [`remove_${r}`]: "1"
93
- }), b(r, ""), m.current?.close();
94
- } catch {
95
- h("We were unable to delete your avatar.");
96
- } finally {
97
- y(!1);
102
+ }, z = async () => {
103
+ d || c || (m(!0), J({
104
+ type: "danger",
105
+ title: "Remove this logo?",
106
+ content: p === "light" ? "Removing this logo means no logo will appear in light mode. This cannot be undone." : w ? "Removing this logo means your light mode logo will appear in dark mode. This cannot be undone." : "Removing this logo means no logo will appear in dark mode. This cannot be undone.",
107
+ confirmButton: "Remove logo",
108
+ onConfirm: async () => {
109
+ try {
110
+ await x({
111
+ [`remove_${o}`]: "1"
112
+ }), y(o, ""), v.current?.close();
113
+ } catch {
114
+ A("We were unable to delete your avatar.");
115
+ } finally {
116
+ m(!1);
117
+ }
118
+ },
119
+ onCancel: () => {
120
+ m(!1);
98
121
  }
99
- }
100
- }, B = () => {
101
- p(null), h("");
122
+ }));
123
+ }, O = () => {
124
+ k(null), A("");
102
125
  };
103
- return /* @__PURE__ */ n(f, { children: [
104
- /* @__PURE__ */ n("div", { className: "pco-org-avatar", children: [
126
+ return /* @__PURE__ */ r(h, { children: [
127
+ /* @__PURE__ */ r("div", { className: "pco-org-avatar", children: [
105
128
  /* @__PURE__ */ e(
106
129
  "button",
107
130
  {
108
131
  type: "button",
109
- onClick: () => m.current?.showModal(),
110
- disabled: c,
111
- className: `pco-org-avatar__button pco-org-avatar__button--${k}`,
112
- children: a ? /* @__PURE__ */ n(f, { children: [
113
- /* @__PURE__ */ e("img", { src: a, alt: `Church logo for ${o}` }),
132
+ onClick: () => v.current?.showModal(),
133
+ disabled: s,
134
+ className: `pco-org-avatar__button pco-org-avatar__button--${p}`,
135
+ children: f ? /* @__PURE__ */ r(h, { children: [
136
+ /* @__PURE__ */ e("img", { src: f, alt: `Church logo for ${t}` }),
114
137
  /* @__PURE__ */ e("div", { className: "tds-btn tds-btn--interaction tds-btn--icononly", children: /* @__PURE__ */ e(
115
138
  "svg",
116
139
  {
@@ -119,10 +142,10 @@ function D({
119
142
  viewBox: "0 0 16 16",
120
143
  fill: "currentColor",
121
144
  "aria-hidden": "true",
122
- children: /* @__PURE__ */ e("path", { d: x })
145
+ children: /* @__PURE__ */ e("path", { d: T })
123
146
  }
124
147
  ) })
125
- ] }) : /* @__PURE__ */ n(f, { children: [
148
+ ] }) : /* @__PURE__ */ r(h, { children: [
126
149
  /* @__PURE__ */ e(
127
150
  "svg",
128
151
  {
@@ -131,7 +154,7 @@ function D({
131
154
  viewBox: "0 0 16 16",
132
155
  fill: "currentColor",
133
156
  "aria-hidden": "true",
134
- children: /* @__PURE__ */ e("path", { d: G })
157
+ children: /* @__PURE__ */ e("path", { d: V })
135
158
  }
136
159
  ),
137
160
  /* @__PURE__ */ e("div", { className: "tds-btn tds-btn--interaction tds-btn--icononly", children: /* @__PURE__ */ e(
@@ -142,37 +165,37 @@ function D({
142
165
  viewBox: "0 0 16 16",
143
166
  fill: "currentColor",
144
167
  "aria-hidden": "true",
145
- children: /* @__PURE__ */ e("path", { d: x })
168
+ children: /* @__PURE__ */ e("path", { d: T })
146
169
  }
147
170
  ) })
148
171
  ] })
149
172
  }
150
173
  ),
151
- /* @__PURE__ */ n("div", { className: "pco-org-avatar__label", children: [
152
- K(k),
174
+ /* @__PURE__ */ r("div", { className: "pco-org-avatar__label", children: [
175
+ ae(p),
153
176
  " mode"
154
177
  ] })
155
178
  ] }),
156
- L(
179
+ q(
157
180
  /* @__PURE__ */ e(
158
181
  "dialog",
159
182
  {
160
- id: g,
161
- ref: m,
183
+ id: M,
184
+ ref: v,
162
185
  className: "pco-org-avatar__dialog",
163
- onClose: B,
164
- children: /* @__PURE__ */ n("form", { onSubmit: S, children: [
165
- /* @__PURE__ */ n("div", { className: "pco-org-avatar__dialog-header", children: [
166
- /* @__PURE__ */ n("h1", { className: "pco-org-avatar__dialog-title", children: [
186
+ onClose: O,
187
+ children: /* @__PURE__ */ r("form", { onSubmit: $, children: [
188
+ /* @__PURE__ */ r("div", { className: "pco-org-avatar__dialog-header", children: [
189
+ /* @__PURE__ */ r("h1", { className: "pco-org-avatar__dialog-title", children: [
167
190
  "Church logo — ",
168
- k,
191
+ p,
169
192
  " mode"
170
193
  ] }),
171
194
  /* @__PURE__ */ e(
172
195
  "button",
173
196
  {
174
197
  type: "button",
175
- onClick: () => m.current?.close(),
198
+ onClick: () => v.current?.close(),
176
199
  className: "pco-org-avatar__dialog-close",
177
200
  "aria-label": "Close modal",
178
201
  children: /* @__PURE__ */ e(
@@ -183,14 +206,14 @@ function D({
183
206
  viewBox: "0 0 16 16",
184
207
  fill: "currentColor",
185
208
  "aria-hidden": "true",
186
- children: /* @__PURE__ */ e("path", { d: E })
209
+ children: /* @__PURE__ */ e("path", { d: X })
187
210
  }
188
211
  )
189
212
  }
190
213
  )
191
214
  ] }),
192
- /* @__PURE__ */ n("div", { className: "pco-org-avatar__dialog-body", children: [
193
- N && /* @__PURE__ */ n("div", { role: "alert", className: "pco-org-avatar__dialog-alert", children: [
215
+ /* @__PURE__ */ r("div", { className: "pco-org-avatar__dialog-body", children: [
216
+ g && /* @__PURE__ */ r("div", { role: "alert", className: "pco-org-avatar-alert pco-org-avatar__dialog-alert", children: [
194
217
  /* @__PURE__ */ e(
195
218
  "svg",
196
219
  {
@@ -199,102 +222,117 @@ function D({
199
222
  viewBox: "0 0 16 16",
200
223
  className: "symbol",
201
224
  "aria-hidden": "true",
202
- children: /* @__PURE__ */ e("path", { d: W })
225
+ children: /* @__PURE__ */ e("path", { d: H })
203
226
  }
204
227
  ),
205
- /* @__PURE__ */ e("p", { children: N })
228
+ /* @__PURE__ */ e("p", { children: g })
206
229
  ] }),
207
230
  /* @__PURE__ */ e(
208
- J,
231
+ Y,
209
232
  {
210
233
  accept: { "image/*": [".jpg", ".jpeg", ".png"] },
211
234
  multiple: !1,
212
- onDropAccepted: R,
235
+ onDropAccepted: P,
213
236
  children: ({
214
237
  getRootProps: u,
215
- getInputProps: i,
216
- isFocused: v,
238
+ getInputProps: l,
239
+ isFocused: b,
217
240
  isDragAccept: j,
218
- isDragReject: C,
219
- isDragActive: A
241
+ isDragReject: R,
242
+ isDragActive: U,
243
+ open: S
220
244
  }) => {
221
- const $ = X(
245
+ const I = Z(
222
246
  "pco-org-avatar__dialog-dropzone",
223
- `pco-org-avatar__dialog-dropzone--${k}`,
247
+ `pco-org-avatar__dialog-dropzone--${p}`,
224
248
  {
225
- "pco-org-avatar__dialog-dropzone--image": l || a,
226
- "pco-org-avatar__dialog-dropzone--focused": v,
249
+ "pco-org-avatar__dialog-dropzone--image": i || f,
250
+ "pco-org-avatar__dialog-dropzone--focused": b,
227
251
  "pco-org-avatar__dialog-dropzone--accepted": j,
228
- "pco-org-avatar__dialog-dropzone--rejected": A && C
252
+ "pco-org-avatar__dialog-dropzone--rejected": U && R
229
253
  }
230
254
  );
231
- return /* @__PURE__ */ n("div", { ...u({ className: $ }), children: [
232
- /* @__PURE__ */ e("input", { ...i() }),
233
- l ? /* @__PURE__ */ e(
234
- "img",
235
- {
236
- src: l.preview,
237
- alt: `Church logo for ${o}`
238
- }
239
- ) : a ? /* @__PURE__ */ e(
240
- "img",
241
- {
242
- src: a,
243
- alt: `Church logo for ${o}`
244
- }
245
- ) : /* @__PURE__ */ n(f, { children: [
246
- /* @__PURE__ */ e(
247
- "svg",
255
+ return /* @__PURE__ */ r(h, { children: [
256
+ /* @__PURE__ */ r("div", { ...u({ className: I }), children: [
257
+ /* @__PURE__ */ e("input", { ...l() }),
258
+ i ? /* @__PURE__ */ e(
259
+ "img",
260
+ {
261
+ src: i.preview,
262
+ alt: `Church logo for ${t}`
263
+ }
264
+ ) : f ? /* @__PURE__ */ e(
265
+ "img",
248
266
  {
249
- width: "64",
250
- height: "64",
251
- viewBox: "0 0 16 16",
252
- "aria-hidden": "true",
253
- children: /* @__PURE__ */ e("path", { d: q })
267
+ src: f,
268
+ alt: `Church logo for ${t}`
254
269
  }
255
- ),
256
- A && C ? /* @__PURE__ */ n(f, { children: [
257
- /* @__PURE__ */ e("h2", { children: "Wrong file type" }),
258
- /* @__PURE__ */ e("p", { children: "Please upload a PNG or JPG file." })
259
- ] }) : /* @__PURE__ */ n(f, { children: [
260
- /* @__PURE__ */ e("h2", { children: "Add church logo" }),
261
- /* @__PURE__ */ e("p", { children: "Use a high-quality image (up to 20mb) with a transparent background." })
270
+ ) : /* @__PURE__ */ r(h, { children: [
271
+ /* @__PURE__ */ e(
272
+ "svg",
273
+ {
274
+ width: "64",
275
+ height: "64",
276
+ viewBox: "0 0 16 16",
277
+ "aria-hidden": "true",
278
+ children: /* @__PURE__ */ e("path", { d: K })
279
+ }
280
+ ),
281
+ U && R ? /* @__PURE__ */ r(h, { children: [
282
+ /* @__PURE__ */ e("h2", { children: "Wrong file type" }),
283
+ /* @__PURE__ */ e("p", { children: "Please upload a PNG or JPG file." })
284
+ ] }) : /* @__PURE__ */ r(h, { children: [
285
+ /* @__PURE__ */ e(B, { open: S }),
286
+ /* @__PURE__ */ e("p", { className: "pco-org-avatar__instructions-sub", children: "Use a high-quality image (up to 20mb) with a transparent background." })
287
+ ] })
262
288
  ] })
263
- ] })
289
+ ] }),
290
+ (i || f) && /* @__PURE__ */ e(B, { open: S })
264
291
  ] });
265
292
  }
266
293
  }
267
294
  )
268
295
  ] }),
269
- /* @__PURE__ */ n("div", { className: "pco-org-avatar__dialog-actions", children: [
270
- a && /* @__PURE__ */ e(
271
- z,
296
+ /* @__PURE__ */ r("div", { className: "pco-org-avatar__dialog-actions", children: [
297
+ a && p === "light" && /* @__PURE__ */ e(
298
+ C,
299
+ {
300
+ label: "Remove logo",
301
+ kind: "secondary-delete",
302
+ onClick: z,
303
+ loading: c,
304
+ disabled: c || d,
305
+ className: "pco-org-avatar__dialog-delete"
306
+ }
307
+ ),
308
+ a && p === "dark" && /* @__PURE__ */ e(
309
+ C,
272
310
  {
273
- label: "Delete logo",
274
- kind: "delete",
275
- onClick: F,
276
- loading: s,
277
- disabled: s || d,
311
+ label: "Use light mode logo",
312
+ kind: "secondary",
313
+ onClick: z,
314
+ loading: c,
315
+ disabled: c || d,
278
316
  className: "pco-org-avatar__dialog-delete"
279
317
  }
280
318
  ),
281
319
  /* @__PURE__ */ e(
282
- z,
320
+ C,
283
321
  {
284
322
  type: "button",
285
323
  label: "Cancel",
286
- onClick: () => m.current?.close(),
287
- disabled: d || s
324
+ onClick: () => v.current?.close(),
325
+ disabled: d || c
288
326
  }
289
327
  ),
290
328
  /* @__PURE__ */ e(
291
- z,
329
+ C,
292
330
  {
293
331
  type: "submit",
294
332
  label: "Update logo",
295
333
  kind: "primary",
296
334
  loading: d,
297
- disabled: !l || d || s
335
+ disabled: !i || d || c
298
336
  }
299
337
  )
300
338
  ] })
@@ -305,45 +343,62 @@ function D({
305
343
  )
306
344
  ] });
307
345
  }
308
- function ne({
346
+ function ge({
309
347
  avatarUrl: a,
310
- darkModeAvatarUrl: o,
311
- orgName: r,
312
- onAvatarUpdate: t,
313
- pcoEnv: c,
314
- readOnly: b,
315
- showDarkModeAvatar: l = !1
348
+ darkModeAvatarUrl: t,
349
+ orgName: o,
350
+ onAvatarUpdate: n,
351
+ pcoEnv: s,
352
+ readOnly: y,
353
+ showDarkModeAvatar: w = !1
316
354
  }) {
317
- const [p, d] = _(a), [w, s] = _(
318
- o
319
- ), y = (h, g) => {
320
- h === "avatar" ? (d(g), t?.("avatar", g)) : h === "dark_mode_avatar" && (s(g), t?.("dark_mode_avatar", g));
355
+ const [i, k] = _(a), [d, N] = _(
356
+ t
357
+ ), c = (m, g) => {
358
+ m === "avatar" ? (k(g), n?.("avatar", g)) : m === "dark_mode_avatar" && (N(g), n?.("dark_mode_avatar", g));
321
359
  };
322
- return /* @__PURE__ */ n("div", { className: "pco-org-avatars", children: [
323
- /* @__PURE__ */ e(
324
- D,
325
- {
326
- avatarUrl: p,
327
- organization: r,
328
- mode: "avatar",
329
- pcoEnv: c,
330
- readOnly: b,
331
- onAvatarUpdate: y
332
- }
333
- ),
334
- l && /* @__PURE__ */ e(
335
- D,
336
- {
337
- avatarUrl: w || p,
338
- organization: r,
339
- mode: "dark_mode_avatar",
340
- pcoEnv: c,
341
- readOnly: b,
342
- onAvatarUpdate: y
343
- }
344
- )
360
+ return /* @__PURE__ */ r(h, { children: [
361
+ /* @__PURE__ */ r("div", { className: "pco-org-avatars", children: [
362
+ /* @__PURE__ */ e(
363
+ F,
364
+ {
365
+ avatarUrl: i,
366
+ organization: o,
367
+ mode: "avatar",
368
+ pcoEnv: s,
369
+ readOnly: y,
370
+ onAvatarUpdate: c
371
+ }
372
+ ),
373
+ w && /* @__PURE__ */ e(
374
+ F,
375
+ {
376
+ avatarUrl: d,
377
+ organization: o,
378
+ mode: "dark_mode_avatar",
379
+ pcoEnv: s,
380
+ readOnly: y,
381
+ onAvatarUpdate: c,
382
+ fallbackAvatarUrl: i
383
+ }
384
+ )
385
+ ] }),
386
+ w && i && !d && /* @__PURE__ */ r("div", { className: "pco-org-avatar-alert", children: [
387
+ /* @__PURE__ */ e(
388
+ "svg",
389
+ {
390
+ width: "16",
391
+ height: "16",
392
+ viewBox: "0 0 16 16",
393
+ className: "symbol",
394
+ "aria-hidden": "true",
395
+ children: /* @__PURE__ */ e("path", { d: Q })
396
+ }
397
+ ),
398
+ /* @__PURE__ */ e("p", { children: "The same logo is being used for both light and dark modes." })
399
+ ] })
345
400
  ] });
346
401
  }
347
402
  export {
348
- ne as OrganizationAvatars
403
+ ge as OrganizationAvatars
349
404
  };
@@ -1,2 +1,2 @@
1
1
  import type { OrganizationAvatarProps } from "./types";
2
- export declare function OrganizationAvatar({ avatarUrl, organization, mode, pcoEnv, readOnly, onAvatarUpdate, }: OrganizationAvatarProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function OrganizationAvatar({ avatarUrl, organization, mode, pcoEnv, readOnly, onAvatarUpdate, fallbackAvatarUrl, }: OrganizationAvatarProps): import("react/jsx-runtime").JSX.Element;
package/dist/style.css CHANGED
@@ -3,23 +3,51 @@
3
3
  gap: var(--t-spacing-1);
4
4
  }
5
5
 
6
+ .pco-org-avatar-alert {
7
+ background: var(--t-fill-color-status-info-ghost);
8
+ border-radius: var(--t-border-radius-lg);
9
+ display: flex;
10
+ gap: var(--t-spacing-2);
11
+ margin-top: var(--t-spacing-2);
12
+ padding: var(--t-spacing-2);
13
+ }
14
+
15
+ .pco-org-avatar-alert svg {
16
+ fill: var(--t-icon-color-status-info);
17
+ flex-shrink: 0;
18
+ height: var(--t-element-size-md);
19
+ position: relative;
20
+ top: 1px;
21
+ width: var(--t-element-size-md);
22
+ }
23
+
24
+ .pco-org-avatar-alert p {
25
+ display: flex;
26
+ flex-direction: column;
27
+ gap: var(--t-spacing-half);
28
+ margin: 0;
29
+ }
30
+
6
31
  .pco-org-avatar {
7
32
  align-items: center;
8
- display: inline-flex;
33
+ display: flex;
9
34
  flex-direction: column;
10
35
  gap: var(--t-spacing-1);
36
+ flex: 1;
37
+ max-width: 175px;
38
+ min-width: 0;
11
39
  }
12
40
 
13
41
  .pco-org-avatar__button {
42
+ aspect-ratio: 175 / 100;
14
43
  background: var(--t-surface-color-canvas);
15
44
  border: var(--t-border-width) dashed var(--t-border-color);
16
45
  border-radius: var(--t-border-radius-lg);
17
46
  color: var(--t-icon-color-dim);
18
47
  cursor: pointer;
19
- height: 100px;
20
48
  padding: var(--t-spacing-1);
21
49
  position: relative;
22
- width: 175px;
50
+ width: 100%;
23
51
  }
24
52
 
25
53
  .pco-org-avatar__button img {
@@ -136,27 +164,11 @@
136
164
 
137
165
  .pco-org-avatar__dialog-alert {
138
166
  background: var(--t-fill-color-status-error-ghost);
139
- border-radius: var(--t-border-radius-lg);
140
- display: flex;
141
- gap: var(--t-spacing-2);
142
- margin-bottom: var(--t-spacing-2);
143
- padding: var(--t-spacing-2);
167
+ margin-block: 0 var(--t-spacing-2);
144
168
  }
145
169
 
146
170
  .pco-org-avatar__dialog-alert svg {
147
171
  fill: var(--t-icon-color-status-error);
148
- flex-shrink: 0;
149
- height: var(--t-element-size-md);
150
- position: relative;
151
- top: 1px;
152
- width: var(--t-element-size-md);
153
- }
154
-
155
- .pco-org-avatar__dialog-alert p {
156
- display: flex;
157
- flex-direction: column;
158
- gap: var(--t-spacing-half);
159
- margin: 0;
160
172
  }
161
173
 
162
174
  .pco-org-avatar__dialog-dropzone {
@@ -178,19 +190,6 @@
178
190
  fill: var(--t-icon-color-status-info);
179
191
  }
180
192
 
181
- .pco-org-avatar__dialog-dropzone h2 {
182
- color: var(--t-text-color-headline);
183
- font-size: var(--t-font-size-md);
184
- font-weight: var(--t-font-weight-normal);
185
- margin: 0;
186
- }
187
-
188
- .pco-org-avatar__dialog-dropzone p {
189
- color: var(--t-text-color-placeholder);
190
- font-size: var(--t-font-size-sm);
191
- margin: 0;
192
- }
193
-
194
193
  .pco-org-avatar__dialog-dropzone img {
195
194
  max-height: 100%;
196
195
  max-width: 100%;
@@ -222,11 +221,25 @@
222
221
  fill: var(--t-fill-color-status-error);
223
222
  }
224
223
 
225
- /* Light/dark backgrounds are hardcoded to match the avatar preview context,
224
+ .pco-org-avatar__instructions {
225
+ font-size: var(--t-font-size-md);
226
+ font-weight: var(--t-font-weight-normal);
227
+ margin: var(--t-spacing-2) 0 0;
228
+ text-align: center;
229
+ }
230
+
231
+ .pco-org-avatar__instructions-sub {
232
+ color: var(--t-text-color-secondary);
233
+ font-size: var(--t-font-size-sm);
234
+ margin: 0;
235
+ }
236
+
237
+ /* Light/dark colors/backgrounds are hardcoded to match the avatar preview context,
226
238
  regardless of the UI theme or whether an avatar is uploaded */
239
+
227
240
  .pco-org-avatar__button--light,
228
241
  .pco-org-avatar__dialog-dropzone--light {
229
- background: hsl(0, 0%, 100%);
242
+ background: hsl(0, 0%, 97%);
230
243
  }
231
244
 
232
245
  .pco-org-avatar__button--light:has(img),
@@ -243,3 +256,11 @@
243
256
  .pco-org-avatar__dialog-dropzone--dark:has(img) {
244
257
  border-color: transparent;
245
258
  }
259
+
260
+ .pco-org-avatar__dialog-dropzone--light .pco-org-avatar__instructions {
261
+ color: hsl(0, 0%, 12%);
262
+ }
263
+
264
+ .pco-org-avatar__dialog-dropzone--dark .pco-org-avatar__instructions {
265
+ color: hsl(0, 0%, 94%);
266
+ }
package/dist/types.d.ts CHANGED
@@ -16,6 +16,7 @@ export type OrganizationAvatarProps = {
16
16
  pcoEnv?: Environment;
17
17
  readOnly?: boolean;
18
18
  onAvatarUpdate: (attrName: AvatarMode, newUrl: string) => void;
19
+ fallbackAvatarUrl?: string;
19
20
  };
20
21
  export type FileWithPreview = File & {
21
22
  preview: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/organization-avatars",
3
- "version": "1.6.3",
3
+ "version": "1.8.0",
4
4
  "description": "Organization avatar upload components for Planning Center apps",
5
5
  "type": "module",
6
6
  "packageManager": "yarn@1.22.22",
@@ -31,14 +31,19 @@
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@planningcenter/icons": "^15.29.1",
34
+ "@planningcenter/sweetest-alert": "^1.0.1",
34
35
  "@planningcenter/tapestry": "^2.10.1",
35
36
  "@planningcenter/url": "^3.2.0",
36
37
  "react": "^18.3.0",
37
38
  "react-dom": "^18.3.0",
38
39
  "react-dropzone": "^14.0.0"
39
40
  },
41
+ "prettier": {
42
+ "semi": false
43
+ },
40
44
  "devDependencies": {
41
45
  "@planningcenter/icons": "^15.29.1",
46
+ "@planningcenter/sweetest-alert": "^1.0.1",
42
47
  "@planningcenter/tapestry": "^2.10.1",
43
48
  "@planningcenter/url": "^3.2.0",
44
49
  "@types/react": "^18.3.0",