@aglyn/plugins-email 1.0.0-beta.219 → 1.0.0-beta.221

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.
Files changed (37) hide show
  1. package/package.json +18 -18
  2. package/src/lib/components/campaign-topic-options-widget.js +2 -2
  3. package/src/lib/components/campaign-topic-options-widget.js.map +1 -1
  4. package/src/lib/components/campaign-topic-select.js +3 -3
  5. package/src/lib/components/campaign-topic-select.js.map +1 -1
  6. package/src/lib/components/dynamic-list-rule-fields.d.ts +20 -4
  7. package/src/lib/components/dynamic-list-rule-fields.js +462 -411
  8. package/src/lib/components/dynamic-list-rule-fields.js.map +1 -1
  9. package/src/lib/components/email-topic-detail.d.ts +1 -1
  10. package/src/lib/components/email-topic-detail.js +5 -5
  11. package/src/lib/components/email-topic-detail.js.map +1 -1
  12. package/src/lib/components/email-topics-card.d.ts +1 -1
  13. package/src/lib/components/email-topics-card.js +6 -5
  14. package/src/lib/components/email-topics-card.js.map +1 -1
  15. package/src/lib/components/use-org-email-topics.d.ts +4 -4
  16. package/src/lib/components/use-org-email-topics.js +6 -5
  17. package/src/lib/components/use-org-email-topics.js.map +1 -1
  18. package/src/lib/list-import.js +2 -2
  19. package/src/lib/list-import.js.map +1 -1
  20. package/src/lib/model/email-topic-catalog.d.ts +36 -0
  21. package/src/lib/model/email-topic-catalog.js +36 -0
  22. package/src/lib/model/email-topic-catalog.js.map +1 -0
  23. package/src/lib/model/index.d.ts +0 -7
  24. package/src/lib/model/index.js +0 -6
  25. package/src/lib/model/index.js.map +1 -1
  26. package/src/lib/server-console.js +6 -2
  27. package/src/lib/server-console.js.map +1 -1
  28. package/src/lib/server-list-import.js +1 -1
  29. package/src/lib/server-list-import.js.map +1 -1
  30. package/src/lib/server-topic-index.d.ts +52 -0
  31. package/src/lib/server-topic-index.js +92 -0
  32. package/src/lib/server-topic-index.js.map +1 -0
  33. package/src/lib/server.js +11 -11
  34. package/src/lib/server.js.map +1 -1
  35. package/src/lib/unsubscribe-link.d.ts +1 -1
  36. package/src/lib/unsubscribe-link.js +1 -1
  37. package/src/lib/unsubscribe-link.js.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-email",
3
- "version": "1.0.0-beta.219",
3
+ "version": "1.0.0-beta.221",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,22 +25,22 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.219",
29
- "@aglyn/shared-data-enums": "1.0.0-beta.219",
30
- "@aglyn/shared-data-mdi": "1.0.0-beta.219",
31
- "@aglyn/shared-data-types": "1.0.0-beta.219",
32
- "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.219",
33
- "@aglyn/shared-ui-jsx": "1.0.0-beta.219",
34
- "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.219",
35
- "@aglyn/shared-ui-next": "1.0.0-beta.219",
36
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.219",
37
- "@aglyn/shared-ui-theme": "1.0.0-beta.219",
38
- "@aglyn/shared-util-email": "1.0.0-beta.219",
39
- "@aglyn/shared-util-http": "1.0.0-beta.219",
40
- "@aglyn/shared-util-timestamp": "1.0.0-beta.219",
41
- "@aglyn/shared-util-tools": "1.0.0-beta.219",
42
- "@aglyn/tenant-data-admin": "1.0.0-beta.219",
43
- "@aglyn/tenant-feature-instance": "1.0.0-beta.219",
28
+ "@aglyn/aglyn": "1.0.0-beta.221",
29
+ "@aglyn/shared-data-enums": "1.0.0-beta.221",
30
+ "@aglyn/shared-data-mdi": "1.0.0-beta.221",
31
+ "@aglyn/shared-data-types": "1.0.0-beta.221",
32
+ "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.221",
33
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.221",
34
+ "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.221",
35
+ "@aglyn/shared-ui-next": "1.0.0-beta.221",
36
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.221",
37
+ "@aglyn/shared-ui-theme": "1.0.0-beta.221",
38
+ "@aglyn/shared-util-email": "1.0.0-beta.221",
39
+ "@aglyn/shared-util-http": "1.0.0-beta.221",
40
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.221",
41
+ "@aglyn/shared-util-tools": "1.0.0-beta.221",
42
+ "@aglyn/tenant-data-admin": "1.0.0-beta.221",
43
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.221",
44
44
  "@swc/helpers": "0.5.23"
45
45
  },
46
46
  "peerDependencies": {
@@ -48,7 +48,7 @@
48
48
  "@mui/x-data-grid": "^9.12.0",
49
49
  "firebase": "^12.18.0",
50
50
  "firebase-admin": "^14.3.0",
51
- "next": "16.3.3",
51
+ "next": "^16.3.3",
52
52
  "react": "^19.2.8"
53
53
  },
54
54
  "sideEffects": false,
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
- import { activeEmailTopics } from "@aglyn/aglyn";
17
+ import { activeSubscriptionTopics } from "@aglyn/aglyn";
18
18
  import { useEffect, useMemo } from "react";
19
19
  import { useOrgEmailTopics } from "./use-org-email-topics.js";
20
20
  /**
@@ -29,7 +29,7 @@ import { useOrgEmailTopics } from "./use-org-email-topics.js";
29
29
  const { topics } = useOrgEmailTopics(hostId, {
30
30
  enabled
31
31
  });
32
- const options = useMemo(()=>activeEmailTopics(topics).map(({ id, name })=>({
32
+ const options = useMemo(()=>activeSubscriptionTopics(topics).map(({ id, name })=>({
33
33
  id,
34
34
  name
35
35
  })), [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/campaign-topic-options-widget.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { activeEmailTopics } from '@aglyn/aglyn'\nimport { useEffect, useMemo } from 'react'\nimport { useOrgEmailTopics } from './use-org-email-topics'\n\n/** What the campaign owner's topic-options zone hands a widget. */\nexport interface CampaignTopicOptionsWidgetProps {\n hostId: string\n enabled: boolean\n onTopics: (topics: ReadonlyArray<{ id: string; name: string }>) => void\n}\n\n/**\n * The streams a campaign can open on, reported to a drawer that lists them.\n *\n * Draws nothing. The catalog is read only while `enabled` — it is 200\n * documents filling one select inside a drawer that is usually shut — and\n * retired topics are left out, for the reason the composer's picker leaves\n * them out: a campaign may not be aimed at a stream nobody can leave.\n */\nexport function CampaignTopicOptionsWidget(\n props: CampaignTopicOptionsWidgetProps,\n) {\n const { hostId, enabled, onTopics } = props\n const { topics } = useOrgEmailTopics(hostId, { enabled })\n const options = useMemo(\n () => activeEmailTopics(topics).map(({ id, name }) => ({ id, name })),\n [topics],\n )\n useEffect(() => {\n onTopics(options)\n }, [options, onTopics])\n return null\n}\n\nexport default CampaignTopicOptionsWidget\n"],"names":["activeEmailTopics","useEffect","useMemo","useOrgEmailTopics","CampaignTopicOptionsWidget","props","hostId","enabled","onTopics","topics","options","map","id","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA,SAASA,iBAAiB,QAAQ,eAAc;AAChD,SAASC,SAAS,EAAEC,OAAO,QAAQ,QAAO;AAC1C,SAASC,iBAAiB,QAAQ,4BAAwB;AAS1D;;;;;;;CAOC,GACD,OAAO,SAASC,2BACdC,KAAsC;IAEtC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAGH;IACtC,MAAM,EAAEI,MAAM,EAAE,GAAGN,kBAAkBG,QAAQ;QAAEC;IAAQ;IACvD,MAAMG,UAAUR,QACd,IAAMF,kBAAkBS,QAAQE,GAAG,CAAC,CAAC,EAAEC,EAAE,EAAEC,IAAI,EAAE,GAAM,CAAA;gBAAED;gBAAIC;YAAK,CAAA,IAClE;QAACJ;KAAO;IAEVR,UAAU;QACRO,SAASE;IACX,GAAG;QAACA;QAASF;KAAS;IACtB,OAAO;AACT;AAEA,eAAeJ,2BAA0B"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/campaign-topic-options-widget.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { activeSubscriptionTopics } from '@aglyn/aglyn'\nimport { useEffect, useMemo } from 'react'\nimport { useOrgEmailTopics } from './use-org-email-topics'\n\n/** What the campaign owner's topic-options zone hands a widget. */\nexport interface CampaignTopicOptionsWidgetProps {\n hostId: string\n enabled: boolean\n onTopics: (topics: ReadonlyArray<{ id: string; name: string }>) => void\n}\n\n/**\n * The streams a campaign can open on, reported to a drawer that lists them.\n *\n * Draws nothing. The catalog is read only while `enabled` — it is 200\n * documents filling one select inside a drawer that is usually shut — and\n * retired topics are left out, for the reason the composer's picker leaves\n * them out: a campaign may not be aimed at a stream nobody can leave.\n */\nexport function CampaignTopicOptionsWidget(\n props: CampaignTopicOptionsWidgetProps,\n) {\n const { hostId, enabled, onTopics } = props\n const { topics } = useOrgEmailTopics(hostId, { enabled })\n const options = useMemo(\n () => activeSubscriptionTopics(topics).map(({ id, name }) => ({ id, name })),\n [topics],\n )\n useEffect(() => {\n onTopics(options)\n }, [options, onTopics])\n return null\n}\n\nexport default CampaignTopicOptionsWidget\n"],"names":["activeSubscriptionTopics","useEffect","useMemo","useOrgEmailTopics","CampaignTopicOptionsWidget","props","hostId","enabled","onTopics","topics","options","map","id","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA,SAASA,wBAAwB,QAAQ,eAAc;AACvD,SAASC,SAAS,EAAEC,OAAO,QAAQ,QAAO;AAC1C,SAASC,iBAAiB,QAAQ,4BAAwB;AAS1D;;;;;;;CAOC,GACD,OAAO,SAASC,2BACdC,KAAsC;IAEtC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAGH;IACtC,MAAM,EAAEI,MAAM,EAAE,GAAGN,kBAAkBG,QAAQ;QAAEC;IAAQ;IACvD,MAAMG,UAAUR,QACd,IAAMF,yBAAyBS,QAAQE,GAAG,CAAC,CAAC,EAAEC,EAAE,EAAEC,IAAI,EAAE,GAAM,CAAA;gBAAED;gBAAIC;YAAK,CAAA,IACzE;QAACJ;KAAO;IAEVR,UAAU;QACRO,SAASE;IACX,GAAG;QAACA;QAASF;KAAS;IACtB,OAAO;AACT;AAEA,eAAeJ,2BAA0B"}
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
17
  import { jsx as _jsx } from "react/jsx-runtime";
18
- import { activeEmailTopics, DEFAULT_CAMPAIGN_TOPIC_ID } from "@aglyn/aglyn";
18
+ import { activeSubscriptionTopics, DEFAULT_SUBSCRIPTION_TOPIC_ID } from "@aglyn/aglyn";
19
19
  import { MenuItem, TextField } from "@mui/material";
20
20
  import { useEffect } from "react";
21
21
  import { useOrgEmailTopics } from "./use-org-email-topics.js";
@@ -44,7 +44,7 @@ import { useOrgEmailTopics } from "./use-org-email-topics.js";
44
44
  */ export function CampaignTopicSelect(props) {
45
45
  const { hostId, value, onChange, disabled } = props;
46
46
  const { topics } = useOrgEmailTopics(hostId);
47
- const options = activeEmailTopics(topics);
47
+ const options = activeSubscriptionTopics(topics);
48
48
  /*
49
49
  * SETTLE ON A REAL OPTION, rather than submitting an empty topic.
50
50
  *
@@ -61,7 +61,7 @@ import { useOrgEmailTopics } from "./use-org-email-topics.js";
61
61
  var _options_find;
62
62
  if (!options.length) return;
63
63
  if (options.some((topic)=>topic.id === value)) return;
64
- const fallback = (_options_find = options.find((topic)=>topic.id === DEFAULT_CAMPAIGN_TOPIC_ID)) != null ? _options_find : options[0];
64
+ const fallback = (_options_find = options.find((topic)=>topic.id === DEFAULT_SUBSCRIPTION_TOPIC_ID)) != null ? _options_find : options[0];
65
65
  onChange(fallback.id);
66
66
  }, [
67
67
  options,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/campaign-topic-select.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { activeEmailTopics, DEFAULT_CAMPAIGN_TOPIC_ID } from '@aglyn/aglyn'\nimport { MenuItem, TextField } from '@mui/material'\nimport { useEffect } from 'react'\nimport { useOrgEmailTopics } from './use-org-email-topics'\n\nexport interface CampaignTopicSelectProps {\n hostId: string\n /** The chosen topic id; empty until the effect below settles it. */\n value: string\n onChange: (topicId: string) => void\n disabled?: boolean\n}\n\n/**\n * WHICH STREAM this campaign belongs to, chosen before it is sent.\n *\n * The topic decides three things at once: which recipients the send skips\n * (anyone who has left this stream), what the preference page linked from the\n * footer highlights as \"this email\", and which stream a resulting unsubscribe\n * is recorded against. So it is a composer field rather than a setting — it is\n * a property of the message, and the same site sends promotions and a\n * newsletter in the same week.\n *\n * ## Why this is its own component\n *\n * The composer (`campaign-composer.tsx`) is a large component under concurrent\n * edit. Keeping the picker separate means it can be dropped into the\n * composer's field stack as one line and carries its own read of the org\n * catalog, rather than threading a fifth collection listener through a\n * component that already holds four.\n *\n * Retired topics are not offered. A campaign may not be composed under a\n * stream nobody can unsubscribe from — but a campaign already SENT under one\n * keeps resolving, which is why retiring is not deleting. See\n * `email-topics-card.tsx`.\n */\nexport function CampaignTopicSelect(props: CampaignTopicSelectProps) {\n const { hostId, value, onChange, disabled } = props\n const { topics } = useOrgEmailTopics(hostId)\n const options = activeEmailTopics(topics)\n\n /*\n * SETTLE ON A REAL OPTION, rather than submitting an empty topic.\n *\n * The send resolves an absent topic to the default anyway, so an empty value\n * would still produce a correct send — but it would produce one whose topic\n * the composer never showed the author. A merchant is entitled to see which\n * stream they are about to mail before they press send, and a select that\n * reads blank while the server has already decided is the shape of a field\n * that lies.\n *\n * It also re-settles when the chosen topic leaves the list, which is what a\n * topic retired in another tab looks like from here.\n */\n useEffect(() => {\n if (!options.length) return\n if (options.some((topic) => topic.id === value)) return\n const fallback =\n options.find((topic) => topic.id === DEFAULT_CAMPAIGN_TOPIC_ID) ??\n options[0]\n onChange(fallback.id)\n }, [options, value, onChange])\n\n return (\n <TextField\n select\n size=\"small\"\n label=\"Topic\"\n value={options.some((topic) => topic.id === value) ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n disabled={disabled || !options.length}\n helperText={\n 'Recipients who have left this stream are skipped, and the ' +\n 'unsubscribe link in this email offers to stop it on its own.'\n }\n >\n {options.map((topic) => (\n <MenuItem key={topic.id} value={topic.id}>\n {topic.name}\n </MenuItem>\n ))}\n </TextField>\n )\n}\n\nexport default CampaignTopicSelect\n"],"names":["activeEmailTopics","DEFAULT_CAMPAIGN_TOPIC_ID","MenuItem","TextField","useEffect","useOrgEmailTopics","CampaignTopicSelect","props","hostId","value","onChange","disabled","topics","options","length","some","topic","id","fallback","find","select","size","label","event","target","helperText","map","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,iBAAiB,EAAEC,yBAAyB,QAAQ,eAAc;AAC3E,SAASC,QAAQ,EAAEC,SAAS,QAAQ,gBAAe;AACnD,SAASC,SAAS,QAAQ,QAAO;AACjC,SAASC,iBAAiB,QAAQ,4BAAwB;AAU1D;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,SAASC,oBAAoBC,KAA+B;IACjE,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAGJ;IAC9C,MAAM,EAAEK,MAAM,EAAE,GAAGP,kBAAkBG;IACrC,MAAMK,UAAUb,kBAAkBY;IAElC;;;;;;;;;;;;GAYC,GACDR,UAAU;YAINS;QAHF,IAAI,CAACA,QAAQC,MAAM,EAAE;QACrB,IAAID,QAAQE,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKR,QAAQ;QACjD,MAAMS,YACJL,gBAAAA,QAAQM,IAAI,CAAC,CAACH,QAAUA,MAAMC,EAAE,KAAKhB,sCAArCY,gBACAA,OAAO,CAAC,EAAE;QACZH,SAASQ,SAASD,EAAE;IACtB,GAAG;QAACJ;QAASJ;QAAOC;KAAS;IAE7B,qBACE,KAACP;QACCiB,MAAM;QACNC,MAAK;QACLC,OAAM;QACNb,OAAOI,QAAQE,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKR,SAASA,QAAQ;QAC7DC,UAAU,CAACa,QAAUb,SAASa,MAAMC,MAAM,CAACf,KAAK;QAChDE,UAAUA,YAAY,CAACE,QAAQC,MAAM;QACrCW,YACE,+DACA;kBAGDZ,QAAQa,GAAG,CAAC,CAACV,sBACZ,KAACd;gBAAwBO,OAAOO,MAAMC,EAAE;0BACrCD,MAAMW,IAAI;eADEX,MAAMC,EAAE;;AAM/B;AAEA,eAAeX,oBAAmB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/campaign-topic-select.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { activeSubscriptionTopics, DEFAULT_SUBSCRIPTION_TOPIC_ID } from '@aglyn/aglyn'\nimport { MenuItem, TextField } from '@mui/material'\nimport { useEffect } from 'react'\nimport { useOrgEmailTopics } from './use-org-email-topics'\n\nexport interface CampaignTopicSelectProps {\n hostId: string\n /** The chosen topic id; empty until the effect below settles it. */\n value: string\n onChange: (topicId: string) => void\n disabled?: boolean\n}\n\n/**\n * WHICH STREAM this campaign belongs to, chosen before it is sent.\n *\n * The topic decides three things at once: which recipients the send skips\n * (anyone who has left this stream), what the preference page linked from the\n * footer highlights as \"this email\", and which stream a resulting unsubscribe\n * is recorded against. So it is a composer field rather than a setting — it is\n * a property of the message, and the same site sends promotions and a\n * newsletter in the same week.\n *\n * ## Why this is its own component\n *\n * The composer (`campaign-composer.tsx`) is a large component under concurrent\n * edit. Keeping the picker separate means it can be dropped into the\n * composer's field stack as one line and carries its own read of the org\n * catalog, rather than threading a fifth collection listener through a\n * component that already holds four.\n *\n * Retired topics are not offered. A campaign may not be composed under a\n * stream nobody can unsubscribe from — but a campaign already SENT under one\n * keeps resolving, which is why retiring is not deleting. See\n * `email-topics-card.tsx`.\n */\nexport function CampaignTopicSelect(props: CampaignTopicSelectProps) {\n const { hostId, value, onChange, disabled } = props\n const { topics } = useOrgEmailTopics(hostId)\n const options = activeSubscriptionTopics(topics)\n\n /*\n * SETTLE ON A REAL OPTION, rather than submitting an empty topic.\n *\n * The send resolves an absent topic to the default anyway, so an empty value\n * would still produce a correct send — but it would produce one whose topic\n * the composer never showed the author. A merchant is entitled to see which\n * stream they are about to mail before they press send, and a select that\n * reads blank while the server has already decided is the shape of a field\n * that lies.\n *\n * It also re-settles when the chosen topic leaves the list, which is what a\n * topic retired in another tab looks like from here.\n */\n useEffect(() => {\n if (!options.length) return\n if (options.some((topic) => topic.id === value)) return\n const fallback =\n options.find((topic) => topic.id === DEFAULT_SUBSCRIPTION_TOPIC_ID) ??\n options[0]\n onChange(fallback.id)\n }, [options, value, onChange])\n\n return (\n <TextField\n select\n size=\"small\"\n label=\"Topic\"\n value={options.some((topic) => topic.id === value) ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n disabled={disabled || !options.length}\n helperText={\n 'Recipients who have left this stream are skipped, and the ' +\n 'unsubscribe link in this email offers to stop it on its own.'\n }\n >\n {options.map((topic) => (\n <MenuItem key={topic.id} value={topic.id}>\n {topic.name}\n </MenuItem>\n ))}\n </TextField>\n )\n}\n\nexport default CampaignTopicSelect\n"],"names":["activeSubscriptionTopics","DEFAULT_SUBSCRIPTION_TOPIC_ID","MenuItem","TextField","useEffect","useOrgEmailTopics","CampaignTopicSelect","props","hostId","value","onChange","disabled","topics","options","length","some","topic","id","fallback","find","select","size","label","event","target","helperText","map","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,wBAAwB,EAAEC,6BAA6B,QAAQ,eAAc;AACtF,SAASC,QAAQ,EAAEC,SAAS,QAAQ,gBAAe;AACnD,SAASC,SAAS,QAAQ,QAAO;AACjC,SAASC,iBAAiB,QAAQ,4BAAwB;AAU1D;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,SAASC,oBAAoBC,KAA+B;IACjE,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAGJ;IAC9C,MAAM,EAAEK,MAAM,EAAE,GAAGP,kBAAkBG;IACrC,MAAMK,UAAUb,yBAAyBY;IAEzC;;;;;;;;;;;;GAYC,GACDR,UAAU;YAINS;QAHF,IAAI,CAACA,QAAQC,MAAM,EAAE;QACrB,IAAID,QAAQE,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKR,QAAQ;QACjD,MAAMS,YACJL,gBAAAA,QAAQM,IAAI,CAAC,CAACH,QAAUA,MAAMC,EAAE,KAAKhB,0CAArCY,gBACAA,OAAO,CAAC,EAAE;QACZH,SAASQ,SAASD,EAAE;IACtB,GAAG;QAACJ;QAASJ;QAAOC;KAAS;IAE7B,qBACE,KAACP;QACCiB,MAAM;QACNC,MAAK;QACLC,OAAM;QACNb,OAAOI,QAAQE,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKR,SAASA,QAAQ;QAC7DC,UAAU,CAACa,QAAUb,SAASa,MAAMC,MAAM,CAACf,KAAK;QAChDE,UAAUA,YAAY,CAACE,QAAQC,MAAM;QACrCW,YACE,+DACA;kBAGDZ,QAAQa,GAAG,CAAC,CAACV,sBACZ,KAACd;gBAAwBO,OAAOO,MAAMC,EAAE;0BACrCD,MAAMW,IAAI;eADEX,MAAMC,EAAE;;AAM/B;AAEA,eAAeX,oBAAmB"}
@@ -157,10 +157,26 @@ export interface DynamicListRuleFieldsProps {
157
157
  */
158
158
  listId?: string;
159
159
  }
160
- export declare function DynamicListRuleFields(props: DynamicListRuleFieldsProps): import("react").JSX.Element;
161
- export declare namespace DynamicListRuleFields {
162
- var displayName: string;
163
- }
160
+ /**
161
+ * The rule's controls.
162
+ *
163
+ * ## A keystroke redraws the field it lands in (AGL-3423)
164
+ *
165
+ * Every control writes the same draft, and the draft is the parent's state,
166
+ * so each keystroke renders this component again. Drawn inline, that redrew
167
+ * every control on the form — 27 inputs and some thousand components for one
168
+ * letter — and a keystroke that costs that much lets typed input queue, which
169
+ * is what turns a chip field's per-commit update into React #185. So every
170
+ * control is memoized and handed only props that keep their identity while
171
+ * what it shows is unchanged: handlers that write through the latest draft,
172
+ * menus built once or from their own data, and `sx` objects that are
173
+ * constants. The sentences above the controls are the one part that is meant
174
+ * to change with every keystroke.
175
+ *
176
+ * The component is memoized as a whole as well, so a keystroke in the page's
177
+ * own fields — the list's name — does not draw the rule at all.
178
+ */
179
+ export declare const DynamicListRuleFields: import("react").MemoExoticComponent<(props: DynamicListRuleFieldsProps) => import("react").JSX.Element>;
164
180
  /**
165
181
  * A draft seeded from a rule that arrives after the form does.
166
182
  *