Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

30 react-aria poc #34

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27,654 changes: 15,664 additions & 11,990 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1",
"chromatic": "^11.3.5",
"deck.gl": "^9.0.8",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
Expand All @@ -60,12 +61,12 @@
"eslint-plugin-storybook": "^0.8.0",
"husky": "^8.0.0",
"lint-staged": "^15.2.2",
"maplibre-gl": "^4.3.2",
"prettier": "^3.2.5",
"prop-types": "^15.8.1",
"react-aria-components": "^1.3.3",
"storybook": "^8.0.10",
"vite": "^5.2.0",
"watch": "^1.0.2",
"deck.gl": "^9.0.8",
"maplibre-gl": "^4.3.2"
"watch": "^1.0.2"
}
}
}
61 changes: 61 additions & 0 deletions src/components/composite-react-aria/LayerButtonReel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
@import "../styles/theme.css";

.react-aria-Toolbar {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably don't need react-aria in the class name.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the default for the components in react-aria

display: flex;
flex-wrap: wrap;
gap: 5px;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that most, if not all of these hard-coded pixel values should either be set globally e.g., gap-small: 5px, inherited (maybe as a flag?), or a property in the component.


&[data-orientation=horizontal] {
flex-direction: row;
}

.react-aria-Group {
display: contents;
}

.react-aria-ToggleButton {
width: 32px;
}
}

.react-aria-Separator {
align-self: stretch;
background-color: var(--border-color);

&[aria-orientation=vertical] {
width: 1px;
margin: 0px 10px;
}
}

.react-aria-Button {
color: var(--text-color);
background: var(--button-background);
border: 1px solid var(--border-color);
border-radius: 4px;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be a global var, or a property. Some applications won't need rounded buttons.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Everything here so far has been taken straight from the react-aria example documentation. We'll need to put more thought into what is specific to each component vs what is applied across all components in the library.

We'll provide some level of customization through a design tokens file and css variables but we'll need to put some thought into the level of customization that will be required (it could potentially be very overwhelming so we'll need to strike a balance)

appearance: none;
vertical-align: middle;
font-size: 1rem;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very small font size. Again should be inherited.

text-align: center;
margin: 0;
outline: none;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the outline can cause a11y issues.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know. Again, this is from an example in the docs but can be changed easily

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this removal is to resolve some janky issues that outlines can have with borders,.It looks like it's addressed in the following rules.

padding: 6px 10px;
text-decoration: none;
min-width: 34.5px;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if decimal pixel values are supported in all browsers, or if they'll behave in a way that makes sense to devs. I would round for clarity.


&[data-pressed] {
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
background: var(--button-background-pressed);
border-color: var(--border-color-pressed);
}

&[data-focus-visible] {
outline: 2px solid var(--focus-ring-color);
outline-offset: -1px;
}

&[data-disabled]{
border-color: var(--border-color-disabled);
color: var(--text-color-disabled);
}
}
24 changes: 24 additions & 0 deletions src/components/composite-react-aria/LayerButtonReel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import PropTypes, { string } from "prop-types";
import React from "react";
import {Button, Toolbar} from 'react-aria-components'
import { Icon } from "../core/Icon";

import "./LayerButtonReel.css";

export const LayerButtonReel = ({ buttons, label }) => (
<Toolbar aria-label={label}>
{buttons &&
Object.keys(buttons).map((key) => (
<Button onClick={buttons[key]}><Icon iconName={key} /></Button>
))}
</Toolbar>
);

LayerButtonReel.propTypes = {
buttons: PropTypes.objectOf(PropTypes.func),
label: string.isRequired,
};

LayerButtonReel.defaultProps = {
buttons: null,
};
66 changes: 66 additions & 0 deletions src/components/composite-react-aria/LayerCard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@import "../styles/theme.css";

.layerCardWrapper {
padding: 0 8px;
}

.react-aria-Switch {
display: flex;
align-items: center;
gap: 0.571rem;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these values come from?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All from examples in the docs

font-size: 1.143rem;
color: var(--text-color);
forced-color-adjust: none;

.indicator {
width: 2rem;
height: 1.143rem;
border: 2px solid var(--border-color);
background: var(--background-color);
border-radius: 1.143rem;
transition: all 200ms;

&:before {
content: '';
display: block;
margin: 0.143rem;
width: 0.857rem;
height: 0.857rem;
background: var(--highlight-background);
border-radius: 16px;
transition: all 200ms;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe needs a prefers-reduced-motion media query.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it essentially be the same, but with no transition?

}
}

&[data-pressed] .indicator {
border-color: var(--border-color-pressed);

&:before {
background: var(--highlight-background-pressed);
}
}

&[data-selected] {
.indicator {
border-color: var(--highlight-background);
background: var(--highlight-background);

&:before {
background: var(--field-background);
transform: translateX(100%);
}
}

&[data-pressed] {
.indicator {
border-color: var(--highlight-background-pressed);
background: var(--highlight-background-pressed);
}
}
}

&[data-focus-visible] .indicator {
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
}
}
32 changes: 32 additions & 0 deletions src/components/composite-react-aria/LayerCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import PropTypes from "prop-types";
import React from "react";
import {Switch} from 'react-aria-components';
import { LayerButtonReel } from "./LayerButtonReel";
import "./LayerCard.css";

export const LayerCard = ({ layerName, isActive, onChange, buttons }) => (
<div className="layerCard">
<div className="layerCardWrapper">
<Switch
isSelected={isActive}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSelected? isActive? I think we need to stick with the HTML nomenclature. I don't know what this does just by looking at it. Is it enabled? i.e., disabled='false'?
I suggest using isDisabled since that is the HTML attribute (if that's what this does).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the react-aria library. It related to making it a "controlled component" in react terms.

The isSelected prop can be used to make the selected state controlled. The onChange event is fired when the user presses the switch, and receives the new value.

For example

I think if we go with this library there are a few quirks like this. They're well documented but deviate from traditional HTML. We'll need to decide if it's worth the tradeoff.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change the isActive prop to isSelected for consistency if we want. As @JBurkinshaw says we will have to catch and update a bunch of these inconsistencies when adopting the library.

onChange={onChange}
>
<div className="indicator" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-closing div? Is this a react thing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think so. The auto formatting changes this <div className="indicator"></div> to this <div className="indicator" />

{layerName}
</Switch>
<LayerButtonReel buttons={buttons} />
</div>
</div>
);

LayerCard.propTypes = {
layerName: PropTypes.string.isRequired,
isActive: PropTypes.bool,
onChange: PropTypes.func.isRequired,
buttons: PropTypes.objectOf(PropTypes.func),
};

LayerCard.defaultProps = {
isActive: false,
buttons: null,
};
69 changes: 69 additions & 0 deletions src/components/composite-react-aria/SliderControl.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@import "../styles/theme.css";

.react-aria-Slider {
display: grid;
grid-template-areas: "label output"
"track track";
grid-template-columns: 1fr auto;
max-width: 300px;
color: var(--text-color);

.react-aria-Label {
grid-area: label;
}

.react-aria-SliderOutput {
grid-area: output;
}

.react-aria-SliderTrack {
grid-area: track;
position: relative;

/* track line */
&:before {
content: '';
display: block;
position: absolute;
background: var(--border-color);
}
}

.react-aria-SliderThumb {
width: 1.429rem;
height: 1.429rem;
border-radius: 50%;
background: var(--highlight-background);
border: 2px solid var(--background-color);
forced-color-adjust: none;

&[data-dragging] {
background: var(--highlight-background-pressed);
}

&[data-focus-visible] {
outline: 2px solid var(--focus-ring-color);
}
}

&[data-orientation=horizontal] {
flex-direction: column;
width: 300px;

.react-aria-SliderTrack {
height: 30px;
width: 100%;

&:before {
height: 3px;
width: 100%;
top: 50%;
transform: translateY(-50%);
}
}

.react-aria-SliderThumb {
top: 50%;
}
}
}
37 changes: 37 additions & 0 deletions src/components/composite-react-aria/SliderControl.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";
import PropTypes from "prop-types";
import { Label, Slider, SliderOutput, SliderThumb, SliderTrack } from 'react-aria-components';
import "./SliderControl.css";

export const SliderControl = ({ title, units, sliderConfig }) => (
<div>
<h3 className="ui-label">{title}</h3>
{sliderConfig &&
sliderConfig.map((slider) => {
const { label, min, max, step, value, onChange } = slider;

return <Slider
minValue={min}
maxValue={max}
step={step}
value={value}
onChange={onChange}
>
<Label>{label}</Label>
<SliderOutput>
{({ state }) => <>{state.getThumbValueLabel(0)}{units}</>}
</SliderOutput>
<SliderTrack>
<SliderThumb name={label}/>
</SliderTrack>
</Slider>
})}
</div>
);

SliderControl.propTypes = {
title: PropTypes.string.isRequired,
units: PropTypes.string.isRequired,
sliderConfig: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string))
.isRequired,
};
9 changes: 9 additions & 0 deletions src/components/composite-react-aria/SwatchLegend.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.swatch-legend {
display: grid;
grid-template-columns: auto;
grid-template-rows: auto;
}

.swatch-container {
margin: 4px;
}
28 changes: 28 additions & 0 deletions src/components/composite-react-aria/SwatchLegend.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import PropTypes from "prop-types";
import { Swatch } from "../core-react-aria/Swatch";
import "./SwatchLegend.css";

export const SwatchLegend = ({ swatches }) => (
<div className="swatch-legend">
{swatches &&
swatches.map((swatch) => {
const { color, label, value, units } = swatch;

return (
<Swatch
key={label}
label={label}
color={color}
value={value}
units={units}
/>
);
})}
</div>
);

SwatchLegend.propTypes = {
swatches: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string))
.isRequired,
};
26 changes: 26 additions & 0 deletions src/components/core-react-aria/Swatch.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* Swatch container */
.swatch-container {
display: grid;
grid-template-columns: min-content 1fr max-content;
align-items: center;
width: 100%;
height: 100%;
}

/* Swatch label */
.swatch-label {
font-size: 14px;
margin: 0 8px;
}

/* Swatch value */
.swatch-value {
font-size: 14px;
}

.react-aria-ColorSwatch {
width: 20px;
height: 20px;
border-radius: 4px;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
}
Loading