Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
123 changes: 117 additions & 6 deletions packages/__docs__/src/Icons/IconsGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@
* SOFTWARE.
*/

import { useState, memo, useCallback, useMemo, useRef } from 'react'
import {
useState,
memo,
useCallback,
useMemo,
useRef,
type SyntheticEvent
} from 'react'
import type { ChangeEvent } from 'react'

import { Heading } from '@instructure/ui-heading'
Expand All @@ -36,13 +43,26 @@ import {
} from '@instructure/ui-a11y-content'
import { Modal } from '@instructure/ui-modal'
import { SourceCodeEditor } from '@instructure/ui-source-code-editor'
import { LucideIcons, CustomIcons } from '@instructure/ui-icons'
import {
LucideIcons,
CustomIcons,
ParchmentGraphics,
ParchmentIcons,
ParchmentVectors
} from '@instructure/ui-icons'
import { XInstUIIcon } from '@instructure/ui-icons'
import { Flex } from '@instructure/ui-flex'
import { SimpleSelect } from '@instructure/ui-simple-select'

type OrgType = 'Instructure' | 'Parchment'

type IconCategory = 'Icons' | 'Graphics' | 'Vectors'

type IconInfo = {
name: string
component: React.ComponentType<any>
org: OrgType
iconCategory?: IconCategory
importPath: string
}

Expand All @@ -56,11 +76,34 @@ const allIcons: IconInfo[] = [
...Object.entries(LucideIcons).map(([name, component]) => ({
name,
component: component as React.ComponentType<any>,
org: 'Instructure' as OrgType,
importPath: '@instructure/ui-icons'
})),
...Object.entries(CustomIcons).map(([name, component]) => ({
name,
component: component as React.ComponentType<any>,
org: 'Instructure' as OrgType,
importPath: '@instructure/ui-icons'
})),
...Object.entries(ParchmentGraphics).map(([name, component]) => ({
name,
component: component as React.ComponentType<any>,
org: 'Parchment' as OrgType,
iconCategory: 'Graphics',
importPath: '@instructure/ui-icons'
})),
...Object.entries(ParchmentIcons).map(([name, component]) => ({
name,
component: component as React.ComponentType<any>,
org: 'Parchment' as OrgType,
iconCategory: 'Icons',
importPath: '@instructure/ui-icons'
})),
...Object.entries(ParchmentVectors).map(([name, component]) => ({
name,
component: component as React.ComponentType<any>,
org: 'Parchment' as OrgType,
iconCategory: 'Vectors',
importPath: '@instructure/ui-icons'
}))
]
Expand Down Expand Up @@ -156,7 +199,12 @@ const IconTile = memo(
IconTile.displayName = 'IconTile'

const IconsGallery = () => {
const orgs = ['Instructure', 'Parchment'] as OrgType[]
const [searchQuery, setSearchQuery] = useState<string>('')
const [selectedOrg, setSelectedOrg] = useState<OrgType>('Instructure')
const [selectedCategory, setSelectedCategory] = useState<
IconCategory | undefined
>(undefined)
const [selectedIcon, setSelectedIcon] = useState<IconInfo | null>(null)
const [rtl, setRtl] = useState<boolean>(false)
const searchTimeoutId = useRef<ReturnType<typeof setTimeout> | null>(null)
Expand All @@ -178,6 +226,26 @@ const IconsGallery = () => {
}, 500)
}

const handleOrgChange = useCallback(
(_e: SyntheticEvent, { value }: { value?: string | number }) => {
setSelectedOrg(value as OrgType)

if (value === 'Parchment') {
setSelectedCategory('Icons')
} else {
setSelectedCategory(undefined)
}
},
[]
)

const handleCategoryChange = useCallback(
(_e: SyntheticEvent, { value }: { value?: string | number }) => {
setSelectedCategory(value as IconCategory)
},
[]
)

const handleBidirectionToggle = useCallback((e: ChangeEvent<any>) => {
setRtl(e.target.checked)
}, [])
Expand All @@ -191,10 +259,18 @@ const IconsGallery = () => {
}, [])

const filteredIcons = useMemo(() => {
if (!searchQuery) return allIcons
const query = searchQuery.toLowerCase()
return allIcons.filter((icon) => icon.name.toLowerCase().includes(query))
}, [searchQuery])
const iconsByOrgAndCategory = allIcons
.filter((icon) => icon.org === selectedOrg)
.filter(
(icon) => !selectedCategory || icon.iconCategory === selectedCategory
)

return searchQuery
? iconsByOrgAndCategory.filter((icon) =>
icon.name.toLowerCase().includes(searchQuery.toLowerCase())
)
: iconsByOrgAndCategory
}, [searchQuery, selectedCategory, selectedOrg])

return (
<div>
Expand All @@ -208,6 +284,17 @@ const IconsGallery = () => {
onChange={handleSearchChange}
renderLabel={<ScreenReaderContent>Icon Name</ScreenReaderContent>}
/>
<SimpleSelect
name="org-select"
renderLabel={<ScreenReaderContent>Icon Format</ScreenReaderContent>}
onChange={handleOrgChange}
>
{orgs.map((org) => (
<SimpleSelect.Option value={org} id={org} key={org}>
{`${org} Icons`}
</SimpleSelect.Option>
))}
</SimpleSelect>
<Checkbox
label={
<AccessibleContent alt="Render icons with right-to-left text direction">
Expand All @@ -219,6 +306,30 @@ const IconsGallery = () => {
onChange={handleBidirectionToggle}
/>
</FormFieldGroup>
{selectedOrg === 'Parchment' && (
<>
<br />
<SimpleSelect
name="category-select"
renderLabel={
<ScreenReaderContent>
Icon Category (only for Parchment)
</ScreenReaderContent>
}
onChange={handleCategoryChange}
>
{['Icons', 'Graphics', 'Vectors'].map((category) => (
<SimpleSelect.Option
value={category}
id={category}
key={category}
>
{category}
</SimpleSelect.Option>
))}
</SimpleSelect>
</>
)}

<div
css={{
Expand Down
37 changes: 37 additions & 0 deletions packages/ui-icons-svg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## ui-icons-svg

Raw SVG source files for the Instructure UI icon set, with no React peer dependency.

### Layout

```
Line/ — line variant icons
Solid/ — solid variant icons
Custom/ — brand and custom icons
```

### Usage

```js
const path = require('path')
const fs = require('fs')

const pkgDir = path.dirname(
require.resolve('@instructure/ui-icons-svg/package.json')
)
const svg = fs.readFileSync(path.join(pkgDir, 'svg/Line/a11y.svg'), 'utf8')
```

Or via subpath import (with a bundler that handles `.svg`):

```js
import a11ySvgUrl from '@instructure/ui-icons-svg/Line/a11y.svg'
```

### Why a separate package

These SVGs power the React components in `@instructure/ui-icons`, but consumers
that don't use React (e.g. Lit-based component libraries) need access to the raw
source files without inheriting a `react` peer dependency. Splitting the SVGs
into their own package keeps the React contract clean for `ui-icons` while
making the source files freely consumable elsewhere.
23 changes: 23 additions & 0 deletions packages/ui-icons-svg/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@instructure/ui-icons-svg",
"version": "11.7.2",
"description": "Raw SVG source files for the Instructure UI icon set.",
"author": "Instructure, Inc. Engineering and Product Design",
"repository": {
"type": "git",
"url": "https://github.com/instructure/instructure-ui.git"
},
"homepage": "https://instructure.github.io/instructure-ui/",
"bugs": "https://github.com/instructure/instructure-ui/issues",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"files": [
"svg"
],
"exports": {
"./*": "./svg/*",
"./package.json": "./package.json"
}
}
Loading
Loading