{"version":3,"file":"AnnouncementWidget-DuSqRFgb.cjs","names":["Info","CheckCircle2","AlertTriangle","Megaphone","Sparkles","useInternalLinks","sanitizeHref","borderWidthClasses","borderColorClasses","ArrowRight","getFontSizeField","getColorField","AlignLeft","AlignCenter","AlignRight","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../widgets/src/widgets/AnnouncementWidget.tsx"],"sourcesContent":["import type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport type { ComponentProps, ComponentType } from \"react\";\nimport type React from \"react\";\nimport {\n  getBorderColorField,\n  getBorderRadiusField,\n  getBorderWidthField,\n  getColorField,\n  getFontSizeField,\n  getPaddingField,\n  borderWidthClasses,\n  borderColorClasses,\n} from \"../core/fields\";\nimport { sanitizeHref } from \"../lib/link-type-utils\";\nimport { useInternalLinks } from \"../hooks/use-internal-link\";\nimport type {\n  BackgroundValue,\n  BorderRadiusOptions,\n  BorderWidthOptions,\n  ColorOptions,\n  FontSizeOptions,\n  PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport {\n  AlertTriangle,\n  AlignCenter,\n  AlignLeft,\n  AlignRight,\n  ArrowRight,\n  CheckCircle2,\n  Info,\n  Megaphone,\n  Sparkles,\n} from \"lucide-react\";\n\ntype TextAlignment = \"left\" | \"center\" | \"right\";\ntype Tone = \"info\" | \"success\" | \"warning\" | \"promo\" | \"neutral\";\n\nconst textAlignmentClasses: Record<TextAlignment, string> = {\n  left: \"text-left\",\n  center: \"text-center\",\n  right: \"text-right\",\n} as const;\n\n// FontSizeOptions like \"md\" don't map to a real Tailwind class via\n// `text-${size}` (there is no `text-md`), so resolve through a table.\nconst fontSizeClasses: Record<FontSizeOptions, string> = {\n  \"2xl\": \"text-2xl\",\n  xl: \"text-xl\",\n  lg: \"text-lg\",\n  md: \"text-base\",\n  sm: \"text-sm\",\n  xs: \"text-xs\",\n};\n\n// Each tone picks a semantic icon; the color is chosen separately (accentColor)\n// so it always resolves to a theme token rather than a hard-coded hue.\nconst toneIcons: Record<Tone, ComponentType<{ className?: string }>> = {\n  info: Info,\n  success: CheckCircle2,\n  warning: AlertTriangle,\n  promo: Megaphone,\n  neutral: Sparkles,\n};\n\ntype AnnouncementWidgetProps = ComponentProps<\"div\"> & {\n  // Title\n  title?: string;\n  titleColor?: ColorOptions;\n  titleFontSize?: FontSizeOptions;\n\n  // Message (optional body)\n  message?: string;\n  messageColor?: ColorOptions;\n\n  // Callout appearance\n  tone?: Tone;\n  showIcon?: boolean;\n  showAccentBar?: boolean;\n  accentColor?: ColorOptions;\n\n  // Optional call to action\n  ctaText?: string;\n  ctaHref?: string;\n\n  // Layout / styling\n  alignment?: TextAlignment;\n  background?: BackgroundValue;\n  padding?: PaddingOptions;\n  borderRadius?: BorderRadiusOptions;\n  borderWidth?: BorderWidthOptions;\n  borderColor?: ColorOptions;\n};\n\n// A text token that matches the surface renders invisibly. Fall back to a token\n// that actually contrasts: \"background\" when the surface itself is \"foreground\",\n// otherwise \"foreground\" (so a foreground-on-foreground pick stays legible too).\nfunction readableOn(color: ColorOptions, surface: ColorOptions): ColorOptions {\n  if (color !== surface) return color;\n  return surface === \"foreground\" ? \"background\" : \"foreground\";\n}\n\nexport function AnnouncementWidget({\n  title = \"Announcement\",\n  titleColor = \"foreground\",\n  titleFontSize = \"lg\",\n  message,\n  messageColor = \"muted\",\n  tone = \"promo\",\n  showIcon = true,\n  showAccentBar = true,\n  accentColor = \"primary\",\n  ctaText,\n  ctaHref,\n  alignment = \"left\",\n  background = {\n    type: \"solid\",\n    color: \"muted\",\n  },\n  padding = 6,\n  borderRadius = \"lg\",\n  borderWidth = \"none\",\n  borderColor = \"muted\",\n  className,\n  ...props\n}: AnnouncementWidgetProps): React.JSX.Element {\n  const resolveInternalLink = useInternalLinks();\n  const backgroundColor = background.color || \"muted\";\n  const backgroundImage =\n    (background.resource?.image_url || background.resource?.imageUrl) &&\n    background.type === \"image\"\n      ? `url(${background.resource.image_url || background.resource.imageUrl})`\n      : \"none\";\n\n  // A text token that matches the background renders invisibly (e.g. muted body\n  // text on a muted surface, or foreground text on a foreground surface). Fall\n  // back to a contrasting token in that case.\n  const titleTextColor = readableOn(titleColor, backgroundColor);\n  const messageTextColor = readableOn(messageColor, backgroundColor);\n\n  const Icon = toneIcons[tone];\n  // Only render a CTA for a safe, well-formed href. sanitizeHref rejects\n  // javascript:/data: and other non-navigational schemes an author could type\n  // into the free-text Button Link field.\n  const safeCtaHref = sanitizeHref(ctaHref?.trim() ?? \"\");\n  const hasCta = !!ctaText?.trim() && !!safeCtaHref;\n  // Same-portal links navigate in-app (keeping a back button) rather than doing\n  // a full document load; external links fall through to the browser.\n  const ctaLink = resolveInternalLink(safeCtaHref);\n\n  // The icon chip + accent stripe read as a left-anchored callout. Centered\n  // alignment stacks the icon above the text instead so nothing looks lopsided.\n  const stacked = alignment === \"center\";\n  const showStripe = showAccentBar && alignment === \"left\";\n\n  return (\n    <div\n      className={`relative flex overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} bg-${backgroundColor} p-${padding} ${className ?? \"\"}`}\n      style={{ backgroundImage }}\n      {...props}\n    >\n      {showStripe && (\n        <span\n          className={`absolute inset-y-0 left-0 w-1 bg-${accentColor}`}\n          aria-hidden\n        />\n      )}\n\n      <div\n        className={`flex w-full gap-3 ${stacked ? \"flex-col items-center\" : \"flex-row items-start\"} ${showStripe ? \"pl-2\" : \"\"}`}\n      >\n        {showIcon && (\n          <span\n            className={`flex size-9 shrink-0 items-center justify-center rounded-full bg-${accentColor}/15 text-${accentColor}`}\n            aria-hidden\n          >\n            <Icon className=\"size-5\" />\n          </span>\n        )}\n\n        <div className={`min-w-0 flex-1 ${textAlignmentClasses[alignment]}`}>\n          <p\n            className={`${fontSizeClasses[titleFontSize]} font-header font-semibold tracking-tight text-pretty text-${titleTextColor}`}\n          >\n            {title}\n          </p>\n\n          {message && (\n            <p\n              className={`mt-1 text-sm leading-relaxed text-pretty text-${messageTextColor}`}\n            >\n              {message}\n            </p>\n          )}\n\n          {hasCta && (\n            <a\n              href={safeCtaHref}\n              onClick={ctaLink.followInternally}\n              className={`mt-3 inline-flex items-center gap-1 text-sm font-semibold tracking-tight text-${accentColor} transition-opacity hover:opacity-80`}\n            >\n              {ctaText}\n              <ArrowRight className=\"size-3.5\" aria-hidden />\n            </a>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport const announcementWidgetPropertySchema: WidgetPropertySchema = {\n  widgetType: \"AnnouncementWidget\",\n  displayName: \"Announcement\",\n  tabsConfig: [{ id: \"content\", label: \"Content\" }],\n  dataSourceTargetProps: [\"title\", \"message\", \"ctaText\", \"ctaHref\"],\n  fields: [\n    // Title group\n    {\n      key: \"title\",\n      label: \"Title\",\n      type: \"text\",\n      description: \"The announcement headline\",\n      defaultValue: \"Announcement\",\n      tab: \"content\",\n      group: \"Title\",\n    },\n    getFontSizeField({\n      key: \"titleFontSize\",\n      label: \"Title Font Size\",\n      description: \"Font size for the title\",\n      defaultValue: \"lg\",\n      tab: \"content\",\n      group: \"Title\",\n    }),\n    getColorField({\n      key: \"titleColor\",\n      label: \"Title Color\",\n      description: \"The color of the title\",\n      defaultValue: \"foreground\",\n      tab: \"content\",\n      group: \"Title\",\n    }),\n    // Message group\n    {\n      key: \"message\",\n      label: \"Message\",\n      type: \"textarea\",\n      description: \"Optional supporting text\",\n      rows: 2,\n      defaultValue: \"\",\n      tab: \"content\",\n      group: \"Message\",\n    },\n    getColorField({\n      key: \"messageColor\",\n      label: \"Message Color\",\n      description: \"The color of the message — kept readable on the background\",\n      defaultValue: \"muted\",\n      tab: \"content\",\n      group: \"Message\",\n    }),\n    // Call to action group\n    {\n      key: \"ctaText\",\n      label: \"Button Text\",\n      type: \"text\",\n      description: \"Optional call-to-action link label (leave blank to hide)\",\n      defaultValue: \"\",\n      tab: \"content\",\n      group: \"Call to Action\",\n    },\n    {\n      key: \"ctaHref\",\n      label: \"Button Link\",\n      type: \"text\",\n      description: \"Where the call-to-action link goes\",\n      defaultValue: \"\",\n      tab: \"content\",\n      group: \"Call to Action\",\n    },\n    // Appearance group\n    {\n      key: \"tone\",\n      label: \"Tone\",\n      type: \"select\",\n      description: \"Picks the leading icon that frames the message\",\n      options: [\n        { label: \"Promo (megaphone)\", value: \"promo\" },\n        { label: \"Info\", value: \"info\" },\n        { label: \"Success (check)\", value: \"success\" },\n        { label: \"Warning\", value: \"warning\" },\n        { label: \"Neutral (sparkle)\", value: \"neutral\" },\n      ],\n      defaultValue: \"promo\",\n      tab: \"content\",\n      group: \"Appearance\",\n    },\n    {\n      key: \"showIcon\",\n      label: \"Show Icon\",\n      type: \"boolean\",\n      description: \"Show the leading tone icon\",\n      defaultValue: true,\n      tab: \"content\",\n      group: \"Appearance\",\n    },\n    {\n      key: \"showAccentBar\",\n      label: \"Show Accent Bar\",\n      type: \"boolean\",\n      description: \"Show the colored accent stripe (left-aligned only)\",\n      defaultValue: true,\n      tab: \"content\",\n      group: \"Appearance\",\n    },\n    getColorField({\n      key: \"accentColor\",\n      label: \"Accent Color\",\n      description: \"Icon, accent stripe and call-to-action color\",\n      defaultValue: \"primary\",\n      tab: \"content\",\n      group: \"Appearance\",\n    }),\n    {\n      key: \"alignment\",\n      label: \"Alignment\",\n      type: \"buttonGroup\",\n      description: \"Text alignment\",\n      options: [\n        { icon: AlignLeft, ariaLabel: \"Align left\", value: \"left\" },\n        { icon: AlignCenter, ariaLabel: \"Align center\", value: \"center\" },\n        { icon: AlignRight, ariaLabel: \"Align right\", value: \"right\" },\n      ],\n      defaultValue: \"left\",\n      tab: \"content\",\n      group: \"Appearance\",\n    },\n    // Style group\n    {\n      type: \"background\",\n      key: \"background\",\n      label: \"Background\",\n      description: \"Background for the announcement banner\",\n      defaultValue: { type: \"solid\", color: \"muted\" },\n      tab: \"content\",\n      group: \"Style\",\n    },\n    getPaddingField({\n      key: \"padding\",\n      label: \"Padding\",\n      description: \"The padding of the banner\",\n      defaultValue: 6,\n      tab: \"content\",\n      group: \"Style\",\n    }),\n    getBorderRadiusField({\n      key: \"borderRadius\",\n      label: \"Border Radius\",\n      description: \"The border radius of the banner\",\n      defaultValue: \"lg\",\n      tab: \"content\",\n      group: \"Style\",\n    }),\n    getBorderWidthField({\n      key: \"borderWidth\",\n      label: \"Border Width\",\n      description: \"Width of the banner border\",\n      defaultValue: \"none\",\n      tab: \"content\",\n      group: \"Style\",\n    }),\n    getBorderColorField({\n      key: \"borderColor\",\n      label: \"Border Color\",\n      description: \"Color of the banner border\",\n      defaultValue: \"muted\",\n      tab: \"content\",\n      group: \"Style\",\n    }),\n  ],\n};\n"],"mappings":";;;;;;;AAsCA,MAAM,uBAAsD;CAC1D,MAAM;CACN,QAAQ;CACR,OAAO;CACR;AAID,MAAM,kBAAmD;CACvD,OAAO;CACP,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAID,MAAM,YAAiE;CACrE,MAAMA,aAAAA;CACN,SAASC,aAAAA;CACT,SAASC,aAAAA;CACT,OAAOC,aAAAA;CACP,SAASC,aAAAA;CACV;AAkCD,SAAS,WAAW,OAAqB,SAAqC;AAC5E,KAAI,UAAU,QAAS,QAAO;AAC9B,QAAO,YAAY,eAAe,eAAe;;AAGnD,SAAgB,mBAAmB,EACjC,QAAQ,gBACR,aAAa,cACb,gBAAgB,MAChB,SACA,eAAe,SACf,OAAO,SACP,WAAW,MACX,gBAAgB,MAChB,cAAc,WACd,SACA,SACA,YAAY,QACZ,aAAa;CACX,MAAM;CACN,OAAO;CACR,EACD,UAAU,GACV,eAAe,MACf,cAAc,QACd,cAAc,SACd,WACA,GAAG,SAC0C;CAC7C,MAAM,sBAAsBC,0BAAAA,kBAAkB;CAC9C,MAAM,kBAAkB,WAAW,SAAS;CAC5C,MAAM,mBACH,WAAW,UAAU,aAAa,WAAW,UAAU,aACxD,WAAW,SAAS,UAChB,OAAO,WAAW,SAAS,aAAa,WAAW,SAAS,SAAS,KACrE;CAKN,MAAM,iBAAiB,WAAW,YAAY,gBAAgB;CAC9D,MAAM,mBAAmB,WAAW,cAAc,gBAAgB;CAElE,MAAM,OAAO,UAAU;CAIvB,MAAM,cAAcC,wBAAAA,aAAa,SAAS,MAAM,IAAI,GAAG;CACvD,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC;CAGtC,MAAM,UAAU,oBAAoB,YAAY;CAIhD,MAAM,UAAU,cAAc;CAC9B,MAAM,aAAa,iBAAiB,cAAc;AAElD,QACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EACE,WAAW,yCAAyC,aAAa,GAAGC,mBAAAA,mBAAmB,aAAa,GAAG,gBAAgB,SAASC,mBAAAA,mBAAmB,eAAe,GAAG,MAAM,gBAAgB,KAAK,QAAQ,GAAG,aAAa;EACxN,OAAO,EAAE,iBAAiB;EAC1B,GAAI;YAHN,CAKG,cACC,iBAAA,GAAA,kBAAA,KAAC,QAAD;GACE,WAAW,oCAAoC;GAC/C,eAAA;GACA,CAAA,EAGJ,iBAAA,GAAA,kBAAA,MAAC,OAAD;GACE,WAAW,qBAAqB,UAAU,0BAA0B,uBAAuB,GAAG,aAAa,SAAS;aADtH,CAGG,YACC,iBAAA,GAAA,kBAAA,KAAC,QAAD;IACE,WAAW,oEAAoE,YAAY,WAAW;IACtG,eAAA;cAEA,iBAAA,GAAA,kBAAA,KAAC,MAAD,EAAM,WAAU,UAAW,CAAA;IACtB,CAAA,EAGT,iBAAA,GAAA,kBAAA,MAAC,OAAD;IAAK,WAAW,kBAAkB,qBAAqB;cAAvD;KACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,WAAW,GAAG,gBAAgB,eAAe,6DAA6D;gBAEzG;MACC,CAAA;KAEH,WACC,iBAAA,GAAA,kBAAA,KAAC,KAAD;MACE,WAAW,iDAAiD;gBAE3D;MACC,CAAA;KAGL,UACC,iBAAA,GAAA,kBAAA,MAAC,KAAD;MACE,MAAM;MACN,SAAS,QAAQ;MACjB,WAAW,iFAAiF,YAAY;gBAH1G,CAKG,SACD,iBAAA,GAAA,kBAAA,KAACC,aAAAA,YAAD;OAAY,WAAU;OAAW,eAAA;OAAc,CAAA,CAC7C;;KAEF;MACF;KACF;;;AAIV,MAAa,mCAAyD;CACpE,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,uBAAuB;EAAC;EAAS;EAAW;EAAW;EAAU;CACjE,QAAQ;EAEN;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACDC,mBAAAA,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAEF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,MAAM;GACN,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACDA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAEF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAED;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,SAAS;IACP;KAAE,OAAO;KAAqB,OAAO;KAAS;IAC9C;KAAE,OAAO;KAAQ,OAAO;KAAQ;IAChC;KAAE,OAAO;KAAmB,OAAO;KAAW;IAC9C;KAAE,OAAO;KAAW,OAAO;KAAW;IACtC;KAAE,OAAO;KAAqB,OAAO;KAAW;IACjD;GACD,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACDA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,SAAS;IACP;KAAE,MAAMC,aAAAA;KAAW,WAAW;KAAc,OAAO;KAAQ;IAC3D;KAAE,MAAMC,aAAAA;KAAa,WAAW;KAAgB,OAAO;KAAU;IACjE;KAAE,MAAMC,aAAAA;KAAY,WAAW;KAAe,OAAO;KAAS;IAC/D;GACD,cAAc;GACd,KAAK;GACL,OAAO;GACR;EAED;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;IAAE,MAAM;IAAS,OAAO;IAAS;GAC/C,KAAK;GACL,OAAO;GACR;EACDC,mBAAAA,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}