{"version":3,"sources":["../../../src/lib/organization-switcher.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckIcon } from \"@radix-ui/react-icons\";\nimport {\n  Box,\n  ChevronDownIcon,\n  Flex,\n  Text,\n  VisuallyHidden,\n} from \"@radix-ui/themes\";\nimport type { OrganizationInfo } from \"../api/endpoint.js\";\nimport cx from \"clsx\";\nimport {\n  getDomProps,\n  isPromiseLike,\n  type WidgetRootDomProps,\n  type WidgetRootState,\n} from \"./utils.js\";\nimport { getErrorMessage } from \"./generic-error.js\";\nimport {\n  Button,\n  type ButtonProps,\n  Skeleton,\n  DropdownMenu,\n} from \"./elements.js\";\nimport { unstable_useWidgetsInvalidator as useWidgetsInvalidator } from \"../utils.js\";\nimport { Translation } from \"./i18n/translation.js\";\nimport { useTranslation } from \"./i18n/use-translation.js\";\n\ntype OrganizationSwitcherVariant = \"ghost\" | \"outline\";\n\n// Rename all uses of `org` to `organization`\ninterface OrganizationSwitcherPassthroughProps extends WidgetRootDomProps {\n  switchToOrganization: ({\n    organizationId,\n  }: {\n    organizationId: string;\n  }) => void | Promise<void>;\n  // Simple props to affect the overall style\n  variant?: OrganizationSwitcherVariant;\n  organizationLabel?: string | null;\n  children?: React.ReactNode;\n  /**\n   * Choose where to truncate organization name in the trigger and dropdown\n   * items.\n   *\n   * - `right`: Truncate the right side of the organization name\n   * - `middle`: Truncate the middle of the organization name, trying to keep\n   *  words whole\n   */\n  truncateBehavior?: \"right\" | \"middle\";\n}\n\ninterface OrganizationSwitcherProps extends OrganizationSwitcherPassthroughProps {\n  organizations: OrganizationInfo[];\n}\n\nconst OrganizationSwitcher: React.FC<OrganizationSwitcherProps> = ({\n  organizations,\n  switchToOrganization,\n  variant,\n  organizationLabel = \"Organizations\",\n  truncateBehavior = \"right\",\n  children,\n  ...domProps\n}) => {\n  const currentOrganization = organizations.find(\n    (organization) => organization.current,\n  );\n  const invalidateAllWidgets = useWidgetsInvalidator();\n\n  // TODO: Possible if the user has no organizations - we should figure out what\n  // to do in this case\n  if (!currentOrganization) {\n    return null;\n  }\n\n  return (\n    <DropdownMenu.Root>\n      <DropdownMenu.Trigger>\n        <Button\n          variant=\"secondary\"\n          {...mapVariantProps(variant)}\n          {...getWidgetRootDomProps(\"resolved\", {\n            ...domProps,\n            // TODO: Remove `OrganizationSwitcherTrigger` in the next major\n            // version. This should follow conventions of all other widgets\n            // using classnames in getWidgetRootDomProps.\n            className: cx(domProps.className, \"OrganizationSwitcherTrigger\"),\n          })}\n        >\n          <Flex\n            align=\"center\"\n            justify=\"between\"\n            gap=\"2\"\n            flexGrow=\"1\"\n            overflow=\"hidden\"\n            minWidth=\"0\"\n          >\n            <TruncatedOrganizationName\n              organizationName={currentOrganization.name}\n              truncateBehavior={truncateBehavior}\n            />\n            <Flex asChild flexShrink=\"0\">\n              <DropdownMenu.TriggerIcon />\n            </Flex>\n          </Flex>\n        </Button>\n      </DropdownMenu.Trigger>\n      <DropdownMenu.Content>\n        <DropdownMenu.Group>\n          {organizationLabel ? (\n            <DropdownMenu.Label>\n              <Text>{organizationLabel}</Text>\n            </DropdownMenu.Label>\n          ) : null}\n          {organizations.map((organization) => (\n            <Flex\n              key={organization.id}\n              asChild\n              pr=\"2\"\n              maxWidth=\"280px\"\n              minWidth=\"180px\"\n            >\n              <DropdownMenu.Item\n                onClick={() => {\n                  if (organization.id !== currentOrganization.id) {\n                    const result = switchToOrganization({\n                      organizationId: organization.id,\n                    });\n                    if (isPromiseLike(result)) {\n                      result.then(invalidateAllWidgets);\n                    } else {\n                      invalidateAllWidgets();\n                    }\n                  }\n                }}\n              >\n                <Flex\n                  justify=\"between\"\n                  align=\"center\"\n                  gap=\"4\"\n                  flexGrow=\"1\"\n                  overflow=\"hidden\"\n                >\n                  <TruncatedOrganizationName\n                    organizationName={organization.name}\n                    truncateBehavior={truncateBehavior}\n                  />\n                  {organization.current && (\n                    <VisuallyHidden>\n                      <Translation\n                        defaultMessage=\"(current)\"\n                        id=\"daFXTP\"\n                        description=\"Label for screen readers indicating current organization\"\n                      />\n                    </VisuallyHidden>\n                  )}\n                  <Flex\n                    aria-hidden\n                    align=\"center\"\n                    justify=\"center\"\n                    flexShrink=\"0\"\n                  >\n                    {organization.current ? (\n                      <CheckIcon width=\"18px\" height=\"18px\" />\n                    ) : (\n                      // make the extra space for\n                      <Box width=\"18px\" height=\"18px\" />\n                    )}\n                  </Flex>\n                </Flex>\n              </DropdownMenu.Item>\n            </Flex>\n          ))}\n        </DropdownMenu.Group>\n        {children}\n      </DropdownMenu.Content>\n    </DropdownMenu.Root>\n  );\n};\n\ninterface OrganizationSwitcherLoadingProps extends WidgetRootDomProps {\n  variant?: OrganizationSwitcherVariant;\n}\n\nconst OrganizationSwitcherLoading: React.FC<\n  OrganizationSwitcherLoadingProps\n> = ({ variant, ...props }: OrganizationSwitcherLoadingProps) => {\n  return (\n    // Always need DropdownMenu.Root to wrap children than may include\n    <Button\n      variant=\"secondary\"\n      disabled\n      {...mapVariantProps(variant)}\n      {...getWidgetRootDomProps(\"loading\", props)}\n    >\n      <Flex align=\"center\" gap=\"2\">\n        <Skeleton>\n          {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n          <Text>Loading...</Text>\n        </Skeleton>\n        <ChevronDownIcon />\n      </Flex>\n    </Button>\n  );\n};\n\ninterface OrganizationSwitcherErrorProps extends WidgetRootDomProps {\n  error: unknown;\n  variant?: OrganizationSwitcherVariant;\n}\n\nfunction OrganizationSwitcherError({\n  error,\n  variant,\n  ...domProps\n}: OrganizationSwitcherErrorProps) {\n  const translate = useTranslation();\n  const { heading } = getErrorMessage(error, translate);\n\n  return (\n    <Button\n      variant=\"secondary\"\n      disabled\n      {...mapVariantProps(variant)}\n      {...getWidgetRootDomProps(\"error\", domProps)}\n    >\n      <Flex align=\"center\" gap=\"2\">\n        <Text>{heading}</Text>\n        <ChevronDownIcon />\n      </Flex>\n    </Button>\n  );\n}\n\nconst MAX_TRUNCATED_SUFFIX_LENGTH = 10;\nconst MIN_TRUNCATED_SUFFIX_LENGTH = 3;\n\nconst WHITE_SPACE_REGEX = /\\s/;\n\nconst getSplitPosition = (organizationName: string) => {\n  if (organizationName.length <= MAX_TRUNCATED_SUFFIX_LENGTH) {\n    return organizationName.length;\n  }\n\n  for (\n    let i = organizationName.length - MAX_TRUNCATED_SUFFIX_LENGTH;\n    i < organizationName.length - MIN_TRUNCATED_SUFFIX_LENGTH;\n    i++\n  ) {\n    if (WHITE_SPACE_REGEX.test(organizationName[i - 1])) {\n      return i;\n    }\n  }\n\n  return organizationName.length - MAX_TRUNCATED_SUFFIX_LENGTH;\n};\n\nconst splitOrganizationName = (organizationName: string) => {\n  const splitPosition = getSplitPosition(organizationName);\n  return [\n    organizationName.slice(0, splitPosition),\n    organizationName.slice(splitPosition),\n  ];\n};\n\nconst TruncatedOrganizationName = ({\n  organizationName,\n  truncateBehavior,\n}: {\n  organizationName: string;\n  truncateBehavior: \"right\" | \"middle\";\n}) => {\n  if (truncateBehavior === \"right\") {\n    return <Text truncate>{organizationName}</Text>;\n  }\n\n  const [organizationNameLeft, organizationNameRight] =\n    splitOrganizationName(organizationName);\n\n  return (\n    <Flex overflow=\"hidden\">\n      <VisuallyHidden>\n        <Text>{organizationName}</Text>\n      </VisuallyHidden>\n      <Text aria-hidden truncate style={{ whiteSpace: \"pre\" }}>\n        {organizationNameLeft}\n      </Text>\n      <Text aria-hidden>{organizationNameRight}</Text>\n    </Flex>\n  );\n};\n\nfunction mapVariantProps(variant?: OrganizationSwitcherVariant): ButtonProps {\n  const variantProps: ButtonProps = {};\n  // Passing `undefined` to the variant prop will result in overrides to props we\n  // set internally. This should be addressed in Radix Themes, but we can\n  // explicitly set it only when it's provided and _not_ undefined.\n  if (variant) {\n    variantProps.unsafe_radixVariant = variant;\n  }\n  return variantProps;\n}\n\nfunction getWidgetRootDomProps(\n  state: WidgetRootState,\n  domProps: WidgetRootDomProps,\n) {\n  return getDomProps({\n    ...domProps,\n    isWidgetRoot: true,\n    widgetId: \"organization-switcher\",\n    widgetState: state,\n  });\n}\n\nexport type {\n  OrganizationSwitcherProps,\n  OrganizationSwitcherLoadingProps,\n  OrganizationSwitcherErrorProps,\n  /** @internal */\n  OrganizationSwitcherPassthroughProps,\n};\nexport {\n  OrganizationSwitcher,\n  OrganizationSwitcherLoading,\n  OrganizationSwitcherError,\n};\n"],"mappings":"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","names":["import_utils","useWidgetsInvalidator","cx"]}