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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/app/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,11 @@ export const createBreakpoints = (breakpointValues) => {
greaterThanOrEqual: (bp1, bp2) => compareBreakpoints(bp1, bp2) >= 0,
};
};

// Shorten words for mobile screens
export const shortenedField = (fieldVal) => {
if (fieldVal.length <= 8) {
return fieldVal;
}
return `${fieldVal.substring(0, 5)}...`;
};
1 change: 1 addition & 0 deletions src/components/CategorySelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const CategorySelector = forwardRef(function CategorySelector(
const options = enabledLabels
.sort((lbl1, lbl2) => compareLabelNames(lbl1.name, lbl2.name))
.map(createOption);

const dispatch = useDispatch();

const defaultHandleBlur = () => dispatch(addLabelEnd());
Expand Down
7 changes: 7 additions & 0 deletions src/components/DatePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import 'react-dates/initialize';
import { SingleDatePicker } from 'react-dates';
import 'react-dates/lib/css/_datepicker.css';
import { inViewportTopHalf } from '../app/utils';
import { useSelector } from 'react-redux';
import { selectGlobalBreakpoint } from '../features/projects/projectsSlice';
import { globalBreakpoints } from '../config';

// NOTE: Date Picker style overrides are in theme/globalStyles.js
// Had to override them there b/c the actual Date Picker element gets
Expand All @@ -27,6 +30,9 @@ const DatePickerWithFormik = ({
setFocusedInput(focused);
};

const currentBreakpoint = useSelector(selectGlobalBreakpoint);
const isSmallScreen = globalBreakpoints.lessThanOrEqual(currentBreakpoint, 'xs');

return (
<div ref={containerEl}>
<SingleDatePicker
Expand All @@ -44,6 +50,7 @@ const DatePickerWithFormik = ({
onFocusChange={onFocusChange}
id="startDate"
small={true}
numberOfMonths={isSmallScreen ? 1 : 2}
hideKeyboardShortcutsPanel={true}
enableOutsideDays={true}
isOutsideRange={() => false}
Expand Down
53 changes: 52 additions & 1 deletion src/components/HamburgerMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { Menu, X, ExternalLinkIcon } from 'lucide-react';
import { Link } from 'react-router-dom';
import Button from './Button.jsx';
import IconButton from './IconButton.jsx';
import { useDispatch, useSelector } from 'react-redux';
import {
selectModalOpen,
selectSelectedProject,
setModalContent,
setModalOpen,
} from '../features/projects/projectsSlice.js';

const openOverlayAnimation = keyframes({
from: { opacity: 0 },
Expand Down Expand Up @@ -94,7 +101,7 @@ const Overlay = styled(Dialog.Overlay, {
});

const Content = styled(Dialog.Content, {
zIndex: '$6',
zIndex: '$5',
width: '100vw',
height: '80dvh',
position: 'fixed',
Expand Down Expand Up @@ -152,8 +159,47 @@ const LinkWithIcon = styled('span', {
},
});

const InternalButton = styled('div', {
...linkStyles,
'&:hover': {
cursor: 'pointer',
color: '$gray12',
borderBottom: '1px solid $gray7',
},
variants: {
disabled: {
true: {
color: '$gray5',
'&:hover': {
pointerEvents: 'none',
cursor: 'auto',
},
},
false: {},
},
},
});

const ModalTrigger = ({ text, disabled, handleClick }) => {
const onClick = disabled ? () => {} : handleClick;

return (
<InternalButton disabled={disabled} onClick={onClick}>
{text}
</InternalButton>
);
};

export const HamburgerMenu = ({ signedIn, appActive, signOut }) => {
const modalOpen = useSelector(selectModalOpen);
const selectedProject = useSelector(selectSelectedProject);
const [isOpen, setIsOpen] = useState(false);
const dispatch = useDispatch();

const handleModalToggle = (content) => {
dispatch(setModalOpen(!modalOpen));
dispatch(setModalContent(content));
};

return (
<MenuRoot onOpenChange={(newState) => setIsOpen(newState)}>
Expand All @@ -170,6 +216,11 @@ export const HamburgerMenu = ({ signedIn, appActive, signOut }) => {
Documentation
</A>
</Dialog.Close>
<ModalTrigger
disabled={!selectedProject}
handleClick={() => handleModalToggle('camera-admin-modal')}
text={'Manage cameras'}
/>
<Dialog.Close asChild>
<SignOut onClick={signOut}>Sign out</SignOut>
</Dialog.Close>
Expand Down
22 changes: 14 additions & 8 deletions src/components/Modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ModalBody = styled('div', {
variants: {
fullHeight: {
true: {
height: 'calc(95vh - $7)',
height: 'calc(95dvh - $7)',
},
},
},
Expand All @@ -32,7 +32,7 @@ const overlayShow = keyframes({
// });

const StyledOverlay = styled(DialogPrimitive.Overlay, {
zIndex: '$4',
zIndex: '$5',
backgroundColor: blackA.blackA9,
position: 'fixed',
inset: 0,
Expand All @@ -57,20 +57,26 @@ const StyledContent = styled(DialogPrimitive.Content, {
variants: {
size: {
sm: {
width: '30vw',
minWidth: '430px',
width: '95dvw',
'@bp1': {
width: '30dvw',
minWidth: '430px',
},
},
md: {
width: '60vw',
width: '95dvw',
'@bp1': {
width: '60dvw',
},
},
lg: {
width: '95vw',
maxHeight: '95vh',
width: '95dvw',
maxHeight: '95dvh',
},
},
fullHeight: {
true: {
height: '95vh',
height: '95dvh',
},
},
},
Expand Down
10 changes: 9 additions & 1 deletion src/components/PermanentActionConfirmation.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import React, { useEffect, useState } from 'react';
import { FieldRow, StandAloneInput } from './Form.jsx';
import { styled } from '../theme/stitches.config.js';

const StyledField = styled(StandAloneInput, {
padding: '$2',
'@bp2': {
padding: '$3',
},
});

const PermanentActionConfirmation = ({ text, setConfirmed }) => {
const [value, setValue] = useState('');
Expand All @@ -15,7 +23,7 @@ const PermanentActionConfirmation = ({ text, setConfirmed }) => {
field:
</p>
<FieldRow>
<StandAloneInput
<StyledField
type="text"
value={value}
onChange={(e) => {
Expand Down
50 changes: 33 additions & 17 deletions src/components/SelectField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,29 @@ import InfoIcon from './InfoIcon';
// TODO: refactor using radix select primative.
// I don't love the incongruous approach to styling react-select forces

const valueContainerStyles = (isSearchable) => {
return (provided) => ({
...provided,
padding: 'var(--space-2)',
'@media screen only and (min-width: 768px)': {
padding: '0px 16px',
},
fontSize: 'var(--fontSizes-3)',
fontFamily: 'var(--fonts-sourceSansPro)',
color: 'var(--colors-gray7)',
...(!isSearchable && {
'> div': { margin: '2px' },
'> input': { display: 'none' },
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For some reason, when isSearchable is false, it still adds an input element to the dom which messes up the padding. This fixes the issue

Copy link
Member

@nathanielrindlaub nathanielrindlaub Oct 17, 2025

Choose a reason for hiding this comment

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

This makes me a bit nervous - have made sure this doesn't have any unintended consequences on desktop on other fields where isSearchable is false? Also for these kinds of notes can you add them as comments so future devs know why this is there?

Updating this: this does make all selects shorter across the app on desktop. This is an interesting catch, but I'm not sure how comfortable I am making this change in general without a thorough evaluation of the implications, and if we do roll with it it should be it's own PR, IMO. How necessary is this to make now? Is it important for the mobile UI?

}),
});
};

const customStyles = {
control: (provided, state) => ({
...provided,
height: '55px',
'@bp2': {
height: '55px',
},
boxSizing: 'border-box',
border: '1px solid',
borderColor: 'var(--colors-border) !important',
Expand All @@ -26,21 +45,6 @@ const customStyles = {
},
}),
}),
input: (provided) => ({
...provided,
'input:focus': {
boxShadow: 'none !important',
transition: 'none',
},
}),
valueContainer: (provided) => ({
...provided,
height: '100%',
padding: '0px 16px',
fontSize: 'var(--fontSizes-3)',
fontFamily: 'var(--fonts-sourceSansPro)',
color: 'var(--colors-gray7)',
}),
menu: (provided) => ({
...provided,
color: 'var(--colors-hiContrast)',
Expand All @@ -57,6 +61,13 @@ const customStyles = {
backgroundColor: 'var(--colors-gray3)',
}),
}),
input: (provided, state) => ({
...provided,
boxShadow: 'none',
...(state.isFocused && {
boxShadow: 'none',
}),
}),
};

const SelectField = ({
Expand All @@ -81,6 +92,11 @@ const SelectField = ({
onBlur(name, true);
};

const styles = {
...customStyles,
...{ valueContainer: valueContainerStyles(isSearchable ?? false) },
};

return (
<div>
{label && (
Expand All @@ -90,7 +106,7 @@ const SelectField = ({
</label>
)}
<Select
styles={customStyles}
styles={styles}
inputId={name}
options={options}
multi={true}
Expand Down
Loading
Loading