diff --git a/packages/ui-img/package.json b/packages/ui-img/package.json
index 8e97ae3470..c98fcb305d 100644
--- a/packages/ui-img/package.json
+++ b/packages/ui-img/package.json
@@ -29,6 +29,7 @@
"@instructure/shared-types": "workspace:*",
"@instructure/ui-dom-utils": "workspace:*",
"@instructure/ui-react-utils": "workspace:*",
+ "@instructure/ui-themes": "workspace:*",
"@instructure/ui-view": "workspace:*"
},
"devDependencies": {
@@ -67,18 +68,18 @@
"default": "./es/exports/a.js"
},
"./v11_7": {
- "src": "./src/exports/a.ts",
- "types": "./types/exports/a.d.ts",
- "import": "./es/exports/a.js",
- "require": "./lib/exports/a.js",
- "default": "./es/exports/a.js"
+ "src": "./src/exports/b.ts",
+ "types": "./types/exports/b.d.ts",
+ "import": "./es/exports/b.js",
+ "require": "./lib/exports/b.js",
+ "default": "./es/exports/b.js"
},
"./latest": {
- "src": "./src/exports/a.ts",
- "types": "./types/exports/a.d.ts",
- "import": "./es/exports/a.js",
- "require": "./lib/exports/a.js",
- "default": "./es/exports/a.js"
+ "src": "./src/exports/b.ts",
+ "types": "./types/exports/b.d.ts",
+ "import": "./es/exports/b.js",
+ "require": "./lib/exports/b.js",
+ "default": "./es/exports/b.js"
}
}
}
diff --git a/packages/ui-img/src/Img/v2/README.md b/packages/ui-img/src/Img/v2/README.md
new file mode 100644
index 0000000000..73a6cdb8ba
--- /dev/null
+++ b/packages/ui-img/src/Img/v2/README.md
@@ -0,0 +1,124 @@
+---
+describes: Img
+---
+
+An accessible image component
+
+```js
+---
+type: example
+---
+
+```
+
+### Margin and display
+
+Use the `margin` prop to add space around ``. Setting the `display` prop to `block` makes
+the image a block-level element.
+
+```js
+---
+type: example
+---
+
+
+
+
+
+```
+
+### Color overlay
+
+The `overlay` prop accepts parameters for `color`, `opacity`, and `blend`.
+
+```js
+---
+type: example
+---
+
+
+
+
+
+```
+
+### Cover
+
+When the `constrain` prop is set to `cover` the image fills the _full_ width and height of its
+containing element, while maintaining the aspect ratio of the source image.
+
+```js
+---
+type: example
+---
+
+
+
+```
+
+### Contain
+
+When the `constrain` prop is set to `contain` the image fits within the width and height of its
+containing element, while maintaining the aspect ratio of the source image.
+
+```js
+---
+type: example
+---
+
+
+
+```
+
+### Grayscale and blur filters
+
+Please note: these should only be used for presentational effects.
+
+```js
+---
+type: example
+---
+
+
+
+
+```
+
+### Guidelines
+
+```js
+---
+type: embed
+---
+
+
+ Contextual images must have alternative text that describes the information or function represented by them
+ Decorative images that do not present important content, are used for layout or non-informative purposes, and do not appear within a link do not need to be presented to screen readers. Decorative and spacer images should have null alternative text (alt="")
+
+
+```
diff --git a/packages/ui-img/src/Img/v1/__tests__/Img.test.tsx b/packages/ui-img/src/Img/v2/__tests__/Img.test.tsx
similarity index 100%
rename from packages/ui-img/src/Img/v1/__tests__/Img.test.tsx
rename to packages/ui-img/src/Img/v2/__tests__/Img.test.tsx
diff --git a/packages/ui-img/src/Img/v2/index.tsx b/packages/ui-img/src/Img/v2/index.tsx
new file mode 100644
index 0000000000..f9680c164d
--- /dev/null
+++ b/packages/ui-img/src/Img/v2/index.tsx
@@ -0,0 +1,139 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - present Instructure, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import { Component } from 'react'
+
+import { View } from '@instructure/ui-view/latest'
+import { passthroughProps } from '@instructure/ui-react-utils'
+
+import { withStyle } from '@instructure/emotion'
+
+import generateStyle from './styles'
+
+import { allowedProps } from './props'
+import type { ImgProps } from './props'
+
+/**
+---
+category: components
+---
+**/
+@withStyle(generateStyle)
+class Img extends Component {
+ static readonly componentId = 'Img'
+
+ static allowedProps = allowedProps
+ static defaultProps = {
+ alt: '',
+ display: 'inline-block',
+ withGrayscale: false,
+ withBlur: false
+ }
+
+ ref: Element | null = null
+
+ handleRef = (el: Element | null) => {
+ const { elementRef } = this.props
+
+ this.ref = el
+
+ if (typeof elementRef === 'function') {
+ elementRef(el)
+ }
+ }
+
+ componentDidMount() {
+ this.props.makeStyles?.()
+ }
+
+ componentDidUpdate() {
+ this.props.makeStyles?.()
+ }
+
+ render() {
+ const {
+ src,
+ alt,
+ margin,
+ display,
+ overlay,
+ withGrayscale,
+ withBlur,
+ constrain,
+ width,
+ height,
+ elementRef,
+ styles,
+ loading,
+ ...props
+ } = this.props
+
+ const a11yProps = {
+ alt: alt || ''
+ }
+
+ const imageProps = {
+ css: styles?.img,
+ src,
+ loading
+ }
+
+ const containerProps = {
+ ...passthroughProps(props),
+ width,
+ height,
+ margin,
+ display,
+ elementRef: this.handleRef
+ }
+
+ if (overlay) {
+ // if a background image is rendered we add the a11y props on the container element
+ const rootProps = {
+ ...containerProps
+ }
+
+ return (
+
+ {/* eslint-disable-next-line jsx-a11y/alt-text*/}
+ {}
+ {overlay && }
+
+ )
+ } else {
+ return (
+
+ )
+ }
+ }
+}
+
+export default Img
+export { Img }
diff --git a/packages/ui-img/src/Img/v2/props.ts b/packages/ui-img/src/Img/v2/props.ts
new file mode 100644
index 0000000000..c0ca8539cc
--- /dev/null
+++ b/packages/ui-img/src/Img/v2/props.ts
@@ -0,0 +1,89 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - present Instructure, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import type { OtherHTMLAttributes } from '@instructure/shared-types'
+import type {
+ Spacing,
+ WithStyleProps,
+ ComponentStyle
+} from '@instructure/emotion'
+import type { NewComponentTypes } from '@instructure/ui-themes'
+
+type ImgOwnProps = {
+ src: string
+ alt?: string
+ display?: 'inline-block' | 'block'
+ /**
+ * Gets passed down to the img component. Same as the native HTML img's loading attribute
+ */
+ loading?: 'eager' | 'lazy'
+ /**
+ * Valid values are `0`, `none`, `auto`, `xxx-small`, `xx-small`, `x-small`,
+ * `small`, `medium`, `large`, `x-large`, `xx-large`. Apply these values via
+ * familiar CSS-like shorthand. For example: `margin="small auto large"`.
+ */
+ margin?: Spacing
+ /**
+ * Valid values for `opacity` are `0` - `10`. Valid values for `blend` are
+ * `normal` (default), `multiply`, `screen`, `overlay`, and `color-burn`.
+ */
+ overlay?: {
+ color: string
+ opacity: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
+ blend?: 'normal' | 'multiply' | 'screen' | 'overlay' | 'color-burn'
+ }
+ withGrayscale?: boolean
+ withBlur?: boolean
+ constrain?: 'cover' | 'contain'
+ elementRef?: (element: Element | null) => void
+ height?: string | number
+ width?: string | number
+}
+
+type PropKeys = keyof ImgOwnProps
+
+type AllowedPropKeys = Readonly>
+
+type ImgProps = ImgOwnProps &
+ WithStyleProps &
+ OtherHTMLAttributes
+
+type ImgStyle = ComponentStyle<'overlay' | 'container' | 'img'>
+const allowedProps: AllowedPropKeys = [
+ 'src',
+ 'alt',
+ 'display',
+ 'loading',
+ 'margin',
+ 'overlay',
+ 'withGrayscale',
+ 'withBlur',
+ 'constrain',
+ 'elementRef',
+ 'height',
+ 'width'
+]
+
+export type { ImgProps, ImgStyle }
+export { allowedProps }
diff --git a/packages/ui-img/src/Img/v2/styles.ts b/packages/ui-img/src/Img/v2/styles.ts
new file mode 100644
index 0000000000..6873e65259
--- /dev/null
+++ b/packages/ui-img/src/Img/v2/styles.ts
@@ -0,0 +1,149 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - present Instructure, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import type { NewComponentTypes } from '@instructure/ui-themes'
+import type { ImgProps, ImgStyle } from './props'
+
+/**
+ * ---
+ * private: true
+ * ---
+ * Generates the style object from the theme and provided additional information
+ * @param {Object} componentTheme The theme variable object.
+ * @param {Object} props the props of the component, the style is applied to
+ * @param {Object} state the state of the component, the style is applied to
+ * @return {Object} The final style object, which will be used in the component
+ */
+const generateStyle = (
+ componentTheme: NewComponentTypes['Img'],
+ props: ImgProps
+): ImgStyle => {
+ const { overlay, withBlur, withGrayscale, constrain } = props
+
+ const isCover = constrain === 'cover'
+ const isContain = constrain === 'contain'
+
+ // if overlay or filters are updated via props,
+ // make the transition look smooth
+ const transitionStyle = {
+ transition: `all ${componentTheme.effectTransitionDuration}`
+ }
+
+ const getFilterStyle = () => {
+ const filters = []
+
+ withBlur && filters.push(`blur(${componentTheme.imageBlurAmount})`)
+ withGrayscale && filters.push('grayscale(1)')
+
+ return filters.length > 0
+ ? {
+ ...transitionStyle,
+ filter: filters.join(' ')
+ }
+ : {
+ filter: 'none'
+ }
+ }
+
+ const fillContainer = {
+ width: '100%',
+ height: '100%'
+ }
+
+ const imgCoverStyle = {
+ objectFit: 'cover',
+ ...fillContainer
+ }
+
+ const imgContainStyle = {
+ objectFit: 'contain',
+ ...fillContainer,
+ ...(overlay && {
+ width: 'auto',
+ height: 'auto',
+ maxWidth: '100%',
+ maxHeight: '100%'
+ })
+ }
+
+ return {
+ overlay: {
+ label: 'img__overlay',
+ position: 'absolute',
+ top: '0px',
+ left: '0px',
+ width: '100%',
+ height: '100%',
+ ...transitionStyle,
+ ...(overlay && {
+ backgroundColor: overlay.color,
+ opacity: overlay.opacity * 0.1,
+ mixBlendMode: overlay.blend ? overlay.blend : undefined
+ })
+ },
+
+ container: {
+ label: 'img__container',
+ ...(overlay && {
+ position: 'relative',
+ overflow:
+ 'hidden' /* stops blurred images extending past overlay borders */
+ }),
+ ...(isCover && fillContainer),
+ ...(isContain && { height: 'inherit' })
+ },
+
+ img: {
+ label: 'img',
+ // reset image styles (initial: all was causing conflicts
+ // View's CSS and overriding height/width attrs)
+ margin: '0',
+ padding: '0',
+ float: 'none',
+ top: 'auto',
+ bottom: 'auto',
+ left: 'auto',
+ right: 'auto',
+ lineHeight: 'normal',
+ position: 'static',
+ transform: 'none',
+ maxHeight: 'none',
+ minHeight: '0',
+ minWidth: '0',
+ maxWidth: '100%',
+
+ ...getFilterStyle(),
+
+ ...(overlay && {
+ // when image is contained in overlay,
+ // avoid extra space at bottom from inline/line-height
+ display: 'block'
+ }),
+ ...(isCover && imgCoverStyle),
+ ...(isContain && imgContainStyle)
+ }
+ }
+}
+
+export default generateStyle
diff --git a/packages/ui-img/src/exports/b.ts b/packages/ui-img/src/exports/b.ts
new file mode 100644
index 0000000000..90d62444bc
--- /dev/null
+++ b/packages/ui-img/src/exports/b.ts
@@ -0,0 +1,25 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - present Instructure, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+export { Img } from '../Img/v2'
+export type { ImgProps } from '../Img/v2/props'
diff --git a/packages/ui-img/tsconfig.build.json b/packages/ui-img/tsconfig.build.json
index b98a354f6f..27eaaae0f3 100644
--- a/packages/ui-img/tsconfig.build.json
+++ b/packages/ui-img/tsconfig.build.json
@@ -12,6 +12,7 @@
{ "path": "../shared-types/tsconfig.build.json" },
{ "path": "../ui-dom-utils/tsconfig.build.json" },
{ "path": "../ui-react-utils/tsconfig.build.json" },
+ { "path": "../ui-themes/tsconfig.build.json" },
{ "path": "../ui-view/tsconfig.build.json" },
{ "path": "../ui-babel-preset/tsconfig.build.json" },
{ "path": "../ui-axe-check/tsconfig.build.json" }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4180ce7132..0e52896fe7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2468,6 +2468,9 @@ importers:
'@instructure/ui-react-utils':
specifier: workspace:*
version: link:../ui-react-utils
+ '@instructure/ui-themes':
+ specifier: workspace:*
+ version: link:../ui-themes
'@instructure/ui-view':
specifier: workspace:*
version: link:../ui-view