<commit_before>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import React, {useEffect, useState} from 'react'

import {View} from '@instructure/ui-view'
import {Flex} from '@instructure/ui-flex'
import {TextInput} from '@instructure/ui-text-input'
import {Popover} from '@instructure/ui-popover'
import {IconArrowOpenDownLine, IconArrowOpenUpLine} from '@instructure/ui-icons'
import {BaseButton, CloseButton, IconButton} from '@instructure/ui-buttons'
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import PreviewIcon from './PreviewIcon'

import formatMessage from '../../../format-message'

const NAMED_COLORS = [
  {color: '#BD3C14', name: formatMessage('Brick')},
  {color: '#FF2717', name: formatMessage('Red')},
  {color: '#E71F63', name: formatMessage('Magenta')},
  {color: '#8F3E97', name: formatMessage('Purple')},
  {color: '#65499D', name: formatMessage('Deep Purple')},
  {color: '#4554A4', name: formatMessage('Indigo')},
  {color: '#1770AB', name: formatMessage('Blue')},
  {color: '#0B9BE3', name: formatMessage('Light Blue')},
  {color: '#06A3B7', name: formatMessage('Cyan')},
  {color: '#009688', name: formatMessage('Teal')},
  {color: '#009606', name: formatMessage('Green')},
  {color: '#8D9900', name: formatMessage('Olive')},
  {color: '#D97900', name: formatMessage('Pumpkin')},
  {color: '#FD5D10', name: formatMessage('Orange')},
  {color: '#F06291', name: formatMessage('Pink')},
  {color: '#000000', name: formatMessage('Black')},
  {color: '#556572', name: formatMessage('Steel Blue')},
  {color: '#6B7780', name: formatMessage('Grey')},
  {color: '#FFFFFF', name: formatMessage('White')},
  null
]

export const ColorInput = ({
  color,
  label,
  name,
  onChange,
  popoverMountNode,
  width = '11rem',
  readonly = false
}) => {
  const [isOpen, setIsOpen] = useState(false)
  const [inputValue, setInputValue] = useState(color)

  const colorName = NAMED_COLORS.find(c => c?.color === color)?.name

  useEffect(() => {
    setInputValue(color)
  }, [color])

  // fire onChange in case value is valid
  const handleColorChange = hex => {
    if (isValidHex(hex)) {
      onChange(hex)
    }
    if (!hex || !hex.length) {
      onChange(null)
    }
    setInputValue(hex)
  }

  // reset the input value on blur if invalid
  const handleInputBlur = () => {
    if (!inputValue || (inputValue.length > 0 && !isValidHex(inputValue))) {
      setInputValue(color)
    }
  }

  function renderPopover() {
    return (
      <Popover
        on="click"
        isShowingContent={isOpen}
        onShowContent={() => setIsOpen(true)}
        onHideContent={() => setIsOpen(false)}
        shouldContainFocus
        shouldReturnFocus
        mountNode={popoverMountNode}
        renderTrigger={
          <IconButton
            screenReaderLabel={formatMessage('View predefined colors')}
            size="small"
            withBackground={false}
            withBorder={false}
            interaction="enabled"
          >
            {isOpen ? <IconArrowOpenUpLine /> : <IconArrowOpenDownLine />}
          </IconButton>
        }
      >
        <CloseButton placement="end" onClick={() => setIsOpen(false)}>
          {formatMessage('Close')}
        </CloseButton>
        <Flex
          alignItems="center"
          as="div"
          justifyItems="center"
          padding="x-large x-small small"
          width="175px"
          wrapItems
        >
          {NAMED_COLORS.map(c => (
            <ColorPreview
              key={`${name}-${c?.color}`}
              color={c?.color}
              name={c?.name}
              disabled={!isOpen}
              onSelect={() => {
                handleColorChange(c?.color)
                setIsOpen(false)
              }}
            />
          ))}
        </Flex>
      </Popover>
    )
  }

  const renderLabel = () => (
    <>
      {label}
      <ScreenReaderContent>{colorName || ''}</ScreenReaderContent>
    </>
  )

  return (
    <View as="div">
      <TextInput
        display="inline-block"
        name={name}
        onBlur={handleInputBlur}
        onChange={(e, value) => handleColorChange(value)}
        placeholder={formatMessage('None')}
        renderBeforeInput={<ColorPreview color={color} disabled margin="0" />}
        renderAfterInput={renderPopover()}
        renderLabel={renderLabel()}
        shouldNotWrap
        value={inputValue || ''}
        width={width}
        interaction={readonly ? 'readonly' : undefined}<commit_msg>IM: Adjust screen reader content for color pickers<commit_after>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import React, {useEffect, useState} from 'react'

import {View} from '@instructure/ui-view'
import {Flex} from '@instructure/ui-flex'
import {TextInput} from '@instructure/ui-text-input'
import {Popover} from '@instructure/ui-popover'
import {IconArrowOpenDownLine, IconArrowOpenUpLine} from '@instructure/ui-icons'
import {BaseButton, CloseButton, IconButton} from '@instructure/ui-buttons'
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import PreviewIcon from './PreviewIcon'

import formatMessage from '../../../format-message'

const NAMED_COLORS = [
  {color: '#BD3C14', name: formatMessage('Brick')},
  {color: '#FF2717', name: formatMessage('Red')},
  {color: '#E71F63', name: formatMessage('Magenta')},
  {color: '#8F3E97', name: formatMessage('Purple')},
  {color: '#65499D', name: formatMessage('Deep Purple')},
  {color: '#4554A4', name: formatMessage('Indigo')},
  {color: '#1770AB', name: formatMessage('Blue')},
  {color: '#0B9BE3', name: formatMessage('Light Blue')},
  {color: '#06A3B7', name: formatMessage('Cyan')},
  {color: '#009688', name: formatMessage('Teal')},
  {color: '#009606', name: formatMessage('Green')},
  {color: '#8D9900', name: formatMessage('Olive')},
  {color: '#D97900', name: formatMessage('Pumpkin')},
  {color: '#FD5D10', name: formatMessage('Orange')},
  {color: '#F06291', name: formatMessage('Pink')},
  {color: '#000000', name: formatMessage('Black')},
  {color: '#556572', name: formatMessage('Steel Blue')},
  {color: '#6B7780', name: formatMessage('Grey')},
  {color: '#FFFFFF', name: formatMessage('White')},
  null
]

export const ColorInput = ({
  color,
  label,
  name,
  onChange,
  popoverMountNode,
  width = '11rem',
  readonly = false
}) => {
  const [isOpen, setIsOpen] = useState(false)
  const [inputValue, setInputValue] = useState(color)

  const colorName = NAMED_COLORS.find(c => c?.color === color)?.name

  useEffect(() => {
    setInputValue(color)
  }, [color])

  // fire onChange in case value is valid
  const handleColorChange = hex => {
    if (isValidHex(hex)) {
      onChange(hex)
    }
    if (!hex || !hex.length) {
      onChange(null)
    }
    setInputValue(hex)
  }

  // reset the input value on blur if invalid
  const handleInputBlur = () => {
    if (!inputValue || (inputValue.length > 0 && !isValidHex(inputValue))) {
      setInputValue(color)
    }
  }

  function renderPopover() {
    return (
      <Popover
        on="click"
        isShowingContent={isOpen}
        onShowContent={() => setIsOpen(true)}
        onHideContent={() => setIsOpen(false)}
        shouldContainFocus
        shouldReturnFocus
        mountNode={popoverMountNode}
        renderTrigger={
          <IconButton
            screenReaderLabel={formatMessage('View predefined colors')}
            size="small"
            withBackground={false}
            withBorder={false}
            interaction="enabled"
          >
            {isOpen ? <IconArrowOpenUpLine /> : <IconArrowOpenDownLine />}
          </IconButton>
        }
      >
        <CloseButton placement="end" onClick={() => setIsOpen(false)}>
          {formatMessage('Close')}
        </CloseButton>
        <Flex
          alignItems="center"
          as="div"
          justifyItems="center"
          padding="x-large x-small small"
          width="175px"
          wrapItems
        >
          {NAMED_COLORS.map(c => (
            <ColorPreview
              key={`${name}-${c?.color}`}
              color={c?.color}
              name={c?.name}
              disabled={!isOpen}
              onSelect={() => {
                handleColorChange(c?.color)
                setIsOpen(false)
              }}
            />
          ))}
        </Flex>
      </Popover>
    )
  }

  const pickerLabel = colorName
    ? formatMessage('Color Picker ({colorName} selected)', {colorName})
    : formatMessage('Color Picker')

  return (
    <View as="div">
      <TextInput
        display="inline-block"
        name={name}
        onBlur={handleInputBlur}
        onChange={(e, value) => handleColorChange(value)}
        placeholder={formatMessage('None')}
        renderBeforeInput={<ColorPreview color={color} disabled margin="0" />}
        renderAfterInput={<span aria-label={pickerLabel}>{renderPopover(colorName)}</span>}
        renderLabel={label}
        shouldNotWrap
        value={inputValue || ''}
        width={width}
        interaction={readonly ? 'readonly' : undefined}
{'fraction_non_alphanumeric': 0.1468768129955521, 'fraction_numerical': 0.018758460645909882, 'mean_word_length': 2.9888160431932125, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4260968', 'n_tokens_mistral': 3200, 'n_tokens_neox': 3113, 'n_words': 760}