@homebound/beam 2.78.2 → 2.79.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { MutableRefObject, ReactNode } from "react";
2
2
  import { ModalProps } from "./Modal/Modal";
3
3
  import { ContentStack } from "./SuperDrawer/useSuperDrawer";
4
- import { CheckFn } from "../types";
4
+ import { CanCloseCheck, CheckFn } from "../types";
5
5
  /** The internal state of our Beam context; see useModal and useSuperDrawer for the public APIs. */
6
6
  export interface BeamContextState {
7
7
  modalState: MutableRefObject<ModalProps | undefined>;
@@ -15,9 +15,9 @@ export interface BeamContextState {
15
15
  /** SuperDrawer contentStack, i.e. the main/non-detail content + 0-N detail contents. */
16
16
  drawerContentStack: MutableRefObject<readonly ContentStack[]>;
17
17
  /** Checks when closing SuperDrawer, for the main/non-detail drawer content. */
18
- drawerCanCloseChecks: MutableRefObject<CheckFn[]>;
18
+ drawerCanCloseChecks: MutableRefObject<CanCloseCheck[]>;
19
19
  /** Checks when closing SuperDrawer Details, a double array to keep per-detail lists. */
20
- drawerCanCloseDetailsChecks: MutableRefObject<CheckFn[][]>;
20
+ drawerCanCloseDetailsChecks: MutableRefObject<CanCloseCheck[][]>;
21
21
  /** The ref for defining the portal element's location for Tab actions */
22
22
  tabActionsRef: MutableRefObject<HTMLDivElement | null>;
23
23
  /** The div for Tab actions to portal into */
@@ -0,0 +1,15 @@
1
+ import { Key } from "react";
2
+ import { Filter } from "./types";
3
+ import { Value } from "../../inputs";
4
+ export declare type DateFilterProps<O, V extends Value, DV extends DateFilterValue<V>> = {
5
+ label: string;
6
+ operations: O[];
7
+ getOperationValue: (o: O) => V;
8
+ getOperationLabel: (o: O) => string;
9
+ defaultValue?: DV;
10
+ };
11
+ export declare type DateFilterValue<V extends Value> = {
12
+ op: V;
13
+ value: Date;
14
+ };
15
+ export declare function dateFilter<O, V extends Key>(props: DateFilterProps<O, V, DateFilterValue<V>>): (key: string) => Filter<DateFilterValue<V>>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dateFilter = void 0;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const BaseFilter_1 = require("./BaseFilter");
7
+ const Label_1 = require("../Label");
8
+ const Css_1 = require("../../Css");
9
+ const inputs_1 = require("../../inputs");
10
+ const defaultTestId_1 = require("../../utils/defaultTestId");
11
+ function dateFilter(props) {
12
+ return (key) => new DateFilter(key, props);
13
+ }
14
+ exports.dateFilter = dateFilter;
15
+ // Custom option that allows for not selecting an operation
16
+ const anyOption = {};
17
+ class DateFilter extends BaseFilter_1.BaseFilter {
18
+ render(value, setValue, tid, inModal, vertical) {
19
+ const { label, operations, getOperationValue, getOperationLabel } = this.props;
20
+ const [focusedEl, setFocusedEl] = (0, react_1.useState)();
21
+ const commonStyles = Css_1.Css.df.aic.fs1.maxwPx(550).bt.bb.bGray300.$;
22
+ // TODO: Maybe make a `CompoundField` component, which could handle the `display: flex` and any sizing requirements per field and focus states.
23
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [vertical && (0, jsx_runtime_1.jsx)(Label_1.Label, { label: label }, void 0), (0, jsx_runtime_1.jsxs)("div", Object.assign({}, this.testId(tid), { css: Css_1.Css.df.$ }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ css: {
24
+ ...commonStyles,
25
+ ...Css_1.Css.bl.borderRadius("4px 0 0 4px").if(focusedEl === "op").bLightBlue700.$,
26
+ } }, { children: (0, jsx_runtime_1.jsx)(inputs_1.SelectField, Object.assign({ compact: true, borderless: true, sizeToContent: true, options: [
27
+ // Always show the 'Any' option
28
+ anyOption,
29
+ ...operations,
30
+ ], getOptionValue: (o) => (o === anyOption ? undefined : getOperationValue(o)), getOptionLabel: (o) => (o === anyOption ? "Any" : getOperationLabel(o)), value: value === null || value === void 0 ? void 0 : value.op, onSelect: (op) =>
31
+ // default the selected date to today if it doesn't exist in the filter's value
32
+ setValue(op ? { op, value: (value === null || value === void 0 ? void 0 : value.value) ? new Date(value.value) : new Date() } : undefined), label: inModal ? `${label} date filter operation` : label, inlineLabel: !inModal && !vertical, hideLabel: inModal || vertical, nothingSelectedText: "Any" }, tid[`${(0, defaultTestId_1.defaultTestId)(this.label)}_dateOperation`], { onFocus: () => setFocusedEl("op"), onBlur: () => setFocusedEl(undefined) }), void 0) }), void 0), (0, jsx_runtime_1.jsx)("div", { css: Css_1.Css.wPx(1).flexNone.bgGray300.if(focusedEl !== undefined).bgLightBlue700.$ }, void 0), (0, jsx_runtime_1.jsx)("div", Object.assign({ css: {
33
+ ...commonStyles,
34
+ ...Css_1.Css.fg1.br.borderRadius("0 4px 4px 0").if(focusedEl === "date").bLightBlue700.$,
35
+ } }, { children: (0, jsx_runtime_1.jsx)(inputs_1.DateField, Object.assign({ borderless: true, compact: true, inlineLabel: true, value: (value === null || value === void 0 ? void 0 : value.value) ? new Date(value.value) : new Date(), label: "Date", onChange: (d) => setValue({ ...value, value: d }), disabled: !value }, tid[`${(0, defaultTestId_1.defaultTestId)(this.label)}_dateField`], { onFocus: () => setFocusedEl("date"), onBlur: () => setFocusedEl(undefined) }), void 0) }), void 0)] }), void 0)] }, void 0));
36
+ }
37
+ }
@@ -2,7 +2,8 @@ import { Key } from "react";
2
2
  import { Filter } from "./types";
3
3
  import { MultiSelectFieldProps } from "../../inputs/MultiSelectField";
4
4
  import { Value } from "../../inputs/Value";
5
- export declare type MultiFilterProps<O, V extends Value> = Omit<MultiSelectFieldProps<O, V>, "values" | "onSelect"> & {
5
+ export declare type MultiFilterProps<O, V extends Value> = Omit<MultiSelectFieldProps<O, V>, "values" | "onSelect" | "label"> & {
6
6
  defaultValue?: V[];
7
+ label?: string;
7
8
  };
8
9
  export declare function multiFilter<O, V extends Key>(props: MultiFilterProps<O, V>): (key: string) => Filter<V[]>;
@@ -2,7 +2,8 @@ import { Key } from "react";
2
2
  import { Filter } from "./types";
3
3
  import { SelectFieldProps } from "../../inputs/SelectField";
4
4
  import { Value } from "../../inputs/Value";
5
- export declare type SingleFilterProps<O, V extends Value> = Omit<SelectFieldProps<O, V>, "value" | "onSelect"> & {
5
+ export declare type SingleFilterProps<O, V extends Value> = Omit<SelectFieldProps<O, V>, "value" | "onSelect" | "label"> & {
6
6
  defaultValue?: V;
7
+ label?: string;
7
8
  };
8
9
  export declare function singleFilter<O, V extends Key>(props: SingleFilterProps<O, V>): (key: string) => Filter<V>;
@@ -1,3 +1,5 @@
1
+ export { dateFilter } from "./DateFilter";
2
+ export type { DateFilterValue } from "./DateFilter";
1
3
  export { multiFilter } from "./MultiFilter";
2
4
  export { singleFilter } from "./SingleFilter";
3
5
  export { booleanFilter } from "./BooleanFilter";
@@ -10,7 +10,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
10
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.toggleFilter = exports.booleanFilter = exports.singleFilter = exports.multiFilter = void 0;
13
+ exports.toggleFilter = exports.booleanFilter = exports.singleFilter = exports.multiFilter = exports.dateFilter = void 0;
14
+ var DateFilter_1 = require("./DateFilter");
15
+ Object.defineProperty(exports, "dateFilter", { enumerable: true, get: function () { return DateFilter_1.dateFilter; } });
14
16
  var MultiFilter_1 = require("./MultiFilter");
15
17
  Object.defineProperty(exports, "multiFilter", { enumerable: true, get: function () { return MultiFilter_1.multiFilter; } });
16
18
  var SingleFilter_1 = require("./SingleFilter");
@@ -1,3 +1,4 @@
1
+ import { DateFilterValue } from "./DateFilter";
1
2
  import { FilterDefs } from "./types";
2
3
  export declare enum Stage {
3
4
  StageOne = "ONE",
@@ -35,8 +36,11 @@ export declare type ProjectFilter = {
35
36
  status?: string[] | null;
36
37
  isTest?: boolean | null;
37
38
  doNotUse?: boolean | null;
39
+ date?: DateFilterValue<string>;
38
40
  };
39
41
  export declare type StageFilter = NonNullable<FilterDefs<ProjectFilter>["stage"]>;
40
42
  export declare type StageSingleFilter = NonNullable<FilterDefs<ProjectFilter>["stageSingle"]>;
43
+ export declare type DateFilter = NonNullable<FilterDefs<ProjectFilter>["date"]>;
41
44
  export declare const stageFilter: StageFilter;
42
45
  export declare const stageSingleFilter: StageSingleFilter;
46
+ export declare const taskDueFilter: DateFilter;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stageSingleFilter = exports.stageFilter = exports.Stage = void 0;
3
+ exports.taskDueFilter = exports.stageSingleFilter = exports.stageFilter = exports.Stage = void 0;
4
+ const DateFilter_1 = require("./DateFilter");
4
5
  const MultiFilter_1 = require("./MultiFilter");
5
6
  const SingleFilter_1 = require("./SingleFilter");
6
7
  var Stage;
@@ -22,3 +23,13 @@ exports.stageSingleFilter = (0, SingleFilter_1.singleFilter)({
22
23
  getOptionValue: (s) => s.code,
23
24
  getOptionLabel: (s) => s.name,
24
25
  });
26
+ exports.taskDueFilter = (0, DateFilter_1.dateFilter)({
27
+ operations: [
28
+ { label: "On", value: "ON" },
29
+ { label: "Before", value: "BEFORE" },
30
+ { label: "After", value: "AFTER" },
31
+ ],
32
+ label: "Task Due",
33
+ getOperationLabel: (o) => o.label,
34
+ getOperationValue: (o) => o.value,
35
+ });
@@ -9,5 +9,5 @@ interface LabelProps {
9
9
  /** An internal helper component for rendering form labels. */
10
10
  export declare function Label(props: LabelProps): import("@emotion/react/jsx-runtime").JSX.Element;
11
11
  /** Used for showing labels within text fields. */
12
- export declare function InlineLabel({ labelProps, label, ...others }: LabelProps): import("@emotion/react/jsx-runtime").JSX.Element;
12
+ export declare function InlineLabel({ labelProps, label, contrast, ...others }: LabelProps): import("@emotion/react/jsx-runtime").JSX.Element;
13
13
  export {};
@@ -12,7 +12,7 @@ function Label(props) {
12
12
  }
13
13
  exports.Label = Label;
14
14
  /** Used for showing labels within text fields. */
15
- function InlineLabel({ labelProps, label, ...others }) {
15
+ function InlineLabel({ labelProps, label, contrast, ...others }) {
16
16
  return ((0, jsx_runtime_1.jsxs)("label", Object.assign({}, labelProps, others, { css: Css_1.Css.smEm.nowrap.gray900.prPx(4).add("color", "currentColor").$ }, { children: [label, ":"] }), void 0));
17
17
  }
18
18
  exports.InlineLabel = InlineLabel;
@@ -1,6 +1,8 @@
1
1
  interface ConfirmCloseModalProps {
2
2
  onClose: () => void;
3
+ discardText?: string;
4
+ continueText?: string;
3
5
  }
4
6
  /** Modal content to appear when a close checks fails */
5
- export declare function ConfirmCloseModal({ onClose }: ConfirmCloseModalProps): import("@emotion/react/jsx-runtime").JSX.Element;
7
+ export declare function ConfirmCloseModal(props: ConfirmCloseModalProps): import("@emotion/react/jsx-runtime").JSX.Element;
6
8
  export {};
@@ -5,7 +5,8 @@ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const src_1 = require("../..");
6
6
  const BeamContext_1 = require("../BeamContext");
7
7
  /** Modal content to appear when a close checks fails */
8
- function ConfirmCloseModal({ onClose }) {
8
+ function ConfirmCloseModal(props) {
9
+ const { onClose, discardText = "Discard Changes", continueText = "Continue Editing" } = props;
9
10
  const { modalState } = (0, BeamContext_1.useBeamContext)();
10
11
  // TODO: Change to closeModal from useModal when canCloseChecks are reset
11
12
  function closeModal() {
@@ -13,10 +14,10 @@ function ConfirmCloseModal({ onClose }) {
13
14
  // after a close and could/will cause other close attempts to fail.
14
15
  modalState.current = undefined;
15
16
  }
16
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(src_1.ModalHeader, { children: "Confirm" }, void 0), (0, jsx_runtime_1.jsx)(src_1.ModalBody, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: src_1.Css.tc.wPx(400).$ }, { children: [(0, jsx_runtime_1.jsx)("p", Object.assign({ css: src_1.Css.lgEm.gray900.mb2.$ }, { children: "Are you sure you want to cancel without saving your changes?" }), void 0), (0, jsx_runtime_1.jsx)("p", Object.assign({ css: src_1.Css.base.gray700.$ }, { children: "Any changes you've made so far will be lost." }), void 0)] }), void 0) }, void 0), (0, jsx_runtime_1.jsxs)(src_1.ModalFooter, { children: [(0, jsx_runtime_1.jsx)(src_1.Button, { variant: "tertiary", label: "Cancel", onClick: closeModal }, void 0), (0, jsx_runtime_1.jsx)(src_1.Button, { label: "Close", onClick: () => {
17
- // The order of these calls doesn't really matter; close this modal and tell the call to do their close
18
- onClose();
19
- closeModal();
20
- } }, void 0)] }, void 0)] }, void 0));
17
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(src_1.ModalHeader, { children: "Confirm" }, void 0), (0, jsx_runtime_1.jsx)(src_1.ModalBody, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: src_1.Css.tc.wPx(400).$ }, { children: [(0, jsx_runtime_1.jsx)("p", Object.assign({ css: src_1.Css.lgEm.gray900.mb2.$ }, { children: "Are you sure you want to cancel?" }), void 0), (0, jsx_runtime_1.jsx)("p", Object.assign({ css: src_1.Css.base.gray700.$ }, { children: "Any data you've entered so far will be lost." }), void 0)] }), void 0) }, void 0), (0, jsx_runtime_1.jsx)(src_1.ModalFooter, Object.assign({ xss: src_1.Css.jcc.$ }, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: src_1.Css.df.fdc.childGap1.aic.$ }, { children: [(0, jsx_runtime_1.jsx)(src_1.Button, { label: continueText, onClick: closeModal }, void 0), (0, jsx_runtime_1.jsx)(src_1.Button, { variant: "tertiary", label: discardText, onClick: () => {
18
+ // The order of these calls doesn't really matter; close this modal and tell the call to do their close
19
+ onClose();
20
+ closeModal();
21
+ } }, void 0)] }), void 0) }), void 0)] }, void 0));
21
22
  }
22
23
  exports.ConfirmCloseModal = ConfirmCloseModal;
@@ -1,4 +1,5 @@
1
1
  import { ReactNode } from "react";
2
+ import { CanCloseCheck } from "../../types";
2
3
  export interface OpenInDrawerOpts {
3
4
  /** Title of the SuperDrawer */
4
5
  title: string;
@@ -44,13 +45,13 @@ export interface UseSuperDrawerHook {
44
45
  * false, a confirmation modal will appear allowing the user to confirm
45
46
  * the action.
46
47
  */
47
- addCanCloseDrawerCheck: (canCloseCheck: () => boolean) => void;
48
+ addCanCloseDrawerCheck: (canCloseCheck: CanCloseCheck) => void;
48
49
  /**
49
50
  * Adds a check when attempting to close a SuperDrawer detail by clicking the
50
51
  * "back" button or calling `closeDrawerDetail()`. If any checks returns
51
52
  * false, a confirmation modal will appear allowing the user to confirm
52
53
  * the action.
53
54
  */
54
- addCanCloseDrawerDetailCheck: (canCloseCheck: () => boolean) => void;
55
+ addCanCloseDrawerDetailCheck: (canCloseCheck: CanCloseCheck) => void;
55
56
  }
56
57
  export declare function useSuperDrawer(): UseSuperDrawerHook;
@@ -12,8 +12,8 @@ function useSuperDrawer() {
12
12
  function canCloseDrawerDetails(i, doChange) {
13
13
  var _a;
14
14
  for (const canCloseDrawerDetail of (_a = canCloseDetailsChecks.current[i]) !== null && _a !== void 0 ? _a : []) {
15
- if (!canCloseDrawerDetail()) {
16
- openModal({ content: (0, jsx_runtime_1.jsx)(ConfirmCloseModal_1.ConfirmCloseModal, { onClose: doChange }, void 0) });
15
+ if (!canClose(canCloseDrawerDetail)) {
16
+ openModal({ content: (0, jsx_runtime_1.jsx)(ConfirmCloseModal_1.ConfirmCloseModal, Object.assign({ onClose: doChange }, canCloseDrawerDetail), void 0) });
17
17
  return false;
18
18
  }
19
19
  }
@@ -40,9 +40,9 @@ function useSuperDrawer() {
40
40
  }
41
41
  // Attempt to close the drawer
42
42
  for (const canCloseDrawer of canCloseChecks.current) {
43
- if (!canCloseDrawer()) {
43
+ if (!canClose(canCloseDrawer)) {
44
44
  openModal({
45
- content: (0, jsx_runtime_1.jsx)(ConfirmCloseModal_1.ConfirmCloseModal, { onClose: doChange }, void 0),
45
+ content: (0, jsx_runtime_1.jsx)(ConfirmCloseModal_1.ConfirmCloseModal, Object.assign({ onClose: doChange }, canCloseDrawer), void 0),
46
46
  });
47
47
  return;
48
48
  }
@@ -139,3 +139,7 @@ function useSuperDrawer() {
139
139
  };
140
140
  }
141
141
  exports.useSuperDrawer = useSuperDrawer;
142
+ function canClose(canCloseCheck) {
143
+ return ((typeof canCloseCheck === "function" && canCloseCheck()) ||
144
+ (typeof canCloseCheck !== "function" && canCloseCheck.check()));
145
+ }
@@ -1,9 +1,10 @@
1
1
  import { FieldState } from "@homebound/form-state/dist/formState";
2
2
  import { MultiSelectFieldProps, Value } from "../inputs";
3
3
  import { HasIdAndName, Optional } from "../types";
4
- export declare type BoundMultiSelectFieldProps<O, V extends Value> = Omit<MultiSelectFieldProps<O, V>, "values" | "onSelect" | "onBlur" | "onFocus"> & {
4
+ export declare type BoundMultiSelectFieldProps<O, V extends Value> = Omit<MultiSelectFieldProps<O, V>, "values" | "onSelect" | "onBlur" | "onFocus" | "label"> & {
5
5
  onSelect?: (values: V[], opts: O[]) => void;
6
6
  field: FieldState<any, V[] | null | undefined>;
7
+ label?: string;
7
8
  };
8
9
  /**
9
10
  * Wraps `MultiSelectField` and binds it to a form field.
@@ -1,9 +1,10 @@
1
1
  import { FieldState } from "@homebound/form-state/dist/formState";
2
2
  import { SelectFieldProps, Value } from "../inputs";
3
3
  import { HasIdAndName, Optional } from "../types";
4
- export declare type BoundSelectFieldProps<T, V extends Value> = Omit<SelectFieldProps<T, V>, "value" | "onSelect" | "onBlur" | "onFocus"> & {
4
+ export declare type BoundSelectFieldProps<T, V extends Value> = Omit<SelectFieldProps<T, V>, "value" | "onSelect" | "onBlur" | "onFocus" | "label"> & {
5
5
  onSelect?: (option: V | undefined) => void;
6
6
  field: FieldState<any, V | null | undefined>;
7
+ label?: string;
7
8
  };
8
9
  /**
9
10
  * Wraps `SelectField` and binds it to a form field.
@@ -18,5 +18,8 @@ export interface DateFieldProps {
18
18
  /** Renders the label inside the input field, i.e. for filters. */
19
19
  inlineLabel?: boolean;
20
20
  placeholder?: string;
21
+ /** If the field should be rendered without a border - This could happen if rendering within a table or as part of a CompoundField */
22
+ borderless?: boolean;
23
+ compact?: boolean;
21
24
  }
22
25
  export declare function DateField(props: DateFieldProps): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -23,16 +23,17 @@ function DateField(props) {
23
23
  const inputWrapRef = (0, react_1.useRef)(null);
24
24
  const buttonRef = (0, react_1.useRef)(null);
25
25
  const overlayRef = (0, react_1.useRef)(null);
26
- const [inputValue, setInputValue] = (0, react_1.useState)(value ? (long && readOnly ? (0, date_fns_1.format)(value, longFormat) : formatDate(value)) : "");
26
+ const showLongFormat = long && readOnly;
27
+ const [inputValue, setInputValue] = (0, react_1.useState)(value ? (showLongFormat ? (0, date_fns_1.format)(value, longFormat) : formatDate(value)) : "");
27
28
  const tid = (0, utils_1.useTestIds)(props, (0, defaultTestId_1.defaultTestId)(label));
28
29
  (0, react_1.useEffect)(() => {
29
- setInputValue(value ? (long && readOnly ? (0, date_fns_1.format)(value, longFormat) : formatDate(value)) : "");
30
+ setInputValue(value ? (showLongFormat ? (0, date_fns_1.format)(value, longFormat) : formatDate(value)) : "");
30
31
  }, [value]);
31
32
  const textFieldProps = {
32
33
  ...others,
33
34
  label,
34
35
  isDisabled: disabled,
35
- isReadOnly: false,
36
+ isReadOnly: readOnly,
36
37
  "aria-haspopup": "dialog",
37
38
  value: inputValue,
38
39
  };
@@ -77,7 +78,10 @@ function DateField(props) {
77
78
  placement: "bottom left",
78
79
  shouldUpdatePosition: true,
79
80
  });
80
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({}, textFieldProps, { readOnly: readOnly, errorMsg: errorMsg, helperText: helperText, required: required, labelProps: labelProps, inputProps: { ...triggerProps, ...inputProps }, inputRef: inputRef, inputWrapRef: inputWrapRef, inlineLabel: inlineLabel, onChange: (v) => {
81
+ // If showing the long format of the date, then leave size undefined. Otherwise we're showing it as "01/01/20", so set size to 8.
82
+ // (Setting the size 8 will currently only impact view of the DateField when placed on the "in page" filters. This is due to other styles set within TextFieldBase)
83
+ const inputSize = showLongFormat ? undefined : 8;
84
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({}, textFieldProps, { readOnly: readOnly, errorMsg: errorMsg, helperText: helperText, required: required, labelProps: labelProps, inputProps: { ...triggerProps, ...inputProps, size: inputSize }, inputRef: inputRef, inputWrapRef: inputWrapRef, inlineLabel: inlineLabel, onChange: (v) => {
81
85
  // hide the calendar if the user is manually entering the date
82
86
  state.close();
83
87
  if (v) {
@@ -2,7 +2,7 @@ import type { NumberFieldAria } from "@react-aria/numberfield";
2
2
  import { InputHTMLAttributes, LabelHTMLAttributes, MutableRefObject, ReactNode, TextareaHTMLAttributes } from "react";
3
3
  import { Xss } from "../Css";
4
4
  import { BeamTextFieldProps } from "../interfaces";
5
- interface TextFieldBaseProps extends Pick<BeamTextFieldProps, "label" | "required" | "readOnly" | "errorMsg" | "onBlur" | "onFocus" | "helperText" | "hideLabel" | "placeholder">, Partial<Pick<BeamTextFieldProps, "onChange">> {
5
+ interface TextFieldBaseProps extends Pick<BeamTextFieldProps, "label" | "required" | "readOnly" | "errorMsg" | "onBlur" | "onFocus" | "helperText" | "hideLabel" | "placeholder" | "borderless">, Partial<Pick<BeamTextFieldProps, "onChange">> {
6
6
  labelProps?: LabelHTMLAttributes<HTMLLabelElement>;
7
7
  inputProps: InputHTMLAttributes<HTMLInputElement> | TextareaHTMLAttributes<HTMLTextAreaElement>;
8
8
  inputRef?: MutableRefObject<HTMLInputElement | HTMLTextAreaElement | null>;
@@ -12,9 +12,11 @@ interface TextFieldBaseProps extends Pick<BeamTextFieldProps, "label" | "require
12
12
  /** TextField specific */
13
13
  compact?: boolean;
14
14
  /** Styles overrides */
15
- xss?: Xss<"textAlign">;
15
+ xss?: Xss<"textAlign" | "fontWeight">;
16
16
  endAdornment?: ReactNode;
17
+ startAdornment?: ReactNode;
17
18
  inlineLabel?: boolean;
19
+ contrast?: boolean;
18
20
  }
19
21
  export declare function TextFieldBase(props: TextFieldBaseProps): import("@emotion/react/jsx-runtime").JSX.Element;
20
22
  export {};
@@ -14,13 +14,44 @@ const useTestIds_1 = require("../utils/useTestIds");
14
14
  // Used by both TextField and TextArea
15
15
  function TextFieldBase(props) {
16
16
  var _a;
17
- const { label, required, labelProps, hideLabel, inputProps, inputRef, inputWrapRef, groupProps, compact = false, errorMsg, helperText, multiline = false, readOnly, onChange, onBlur, onFocus, xss, endAdornment, inlineLabel, } = props;
17
+ const { label, required, labelProps, hideLabel, inputProps, inputRef, inputWrapRef, groupProps, compact = false, errorMsg, helperText, multiline = false, readOnly, onChange, onBlur, onFocus, xss, endAdornment, startAdornment, inlineLabel, borderless = false, contrast = false, } = props;
18
18
  const errorMessageId = `${inputProps.id}-error`;
19
19
  const labelSuffix = (0, labelUtils_1.getLabelSuffix)(required);
20
20
  const ElementType = multiline ? "textarea" : "input";
21
- const tid = (0, useTestIds_1.useTestIds)(props, (0, defaultTestId_1.defaultTestId)(label || "textField"));
21
+ const tid = (0, useTestIds_1.useTestIds)(props, (0, defaultTestId_1.defaultTestId)(label));
22
22
  const [isFocused, setIsFocused] = (0, react_1.useState)(false);
23
+ const { hoverProps, isHovered } = (0, react_aria_1.useHover)({});
23
24
  const { focusWithinProps } = (0, react_aria_1.useFocusWithin)({ onFocusWithinChange: setIsFocused });
25
+ const maybeSmaller = borderless ? 2 : 0;
26
+ const fieldHeight = 40;
27
+ const compactFieldHeight = 32;
28
+ const fieldStyles = {
29
+ container: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$,
30
+ inputWrapper: {
31
+ ...Css_1.Css.sm.df.aic.br4.px1.w100
32
+ .hPx(fieldHeight - maybeSmaller)
33
+ .if(compact)
34
+ .hPx(compactFieldHeight - maybeSmaller).$,
35
+ ...Css_1.Css.bgWhite.bGray300.gray900.if(contrast).bgGray700.bGray700.white.$,
36
+ ...(!borderless ? Css_1.Css.ba.$ : {}),
37
+ },
38
+ inputWrapperReadOnly: {
39
+ ...Css_1.Css.sm.df.aic.w100
40
+ .mhPx(fieldHeight - maybeSmaller)
41
+ .if(compact)
42
+ .mhPx(compactFieldHeight - maybeSmaller).$,
43
+ ...Css_1.Css.gray900.if(contrast).white.$,
44
+ },
45
+ input: {
46
+ ...Css_1.Css.w100.mw0.outline0.br4.fg1.$,
47
+ // Not using Truss's inline `if` statement here because `addIn` properties do not respect the if statement.
48
+ ...(!contrast ? Css_1.Css.bgWhite.$ : Css_1.Css.bgGray700.addIn("&::selection", Css_1.Css.bgGray800.$).$),
49
+ },
50
+ hover: Css_1.Css.bgGray100.if(contrast).bgGray600.bGray600.$,
51
+ focus: Css_1.Css.bLightBlue700.if(contrast).bLightBlue500.$,
52
+ disabled: Css_1.Css.cursorNotAllowed.gray400.bgGray100.if(contrast).gray500.bgGray700.$,
53
+ error: Css_1.Css.bRed600.if(contrast).bRed400.$,
54
+ };
24
55
  // Watch for each WIP change, convert empty to undefined, and call the user's onChange
25
56
  function onDomChange(e) {
26
57
  if (onChange) {
@@ -34,25 +65,26 @@ function TextFieldBase(props) {
34
65
  const onFocusChained = (0, react_aria_1.chain)((e) => {
35
66
  e.target.select();
36
67
  }, onFocus);
37
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$ }, groupProps, focusWithinProps, { children: [label && !hideLabel && !inlineLabel && ((0, jsx_runtime_1.jsx)(Label_1.Label, Object.assign({ labelProps: labelProps, label: label, suffix: labelSuffix }, tid.label), void 0)), readOnly && ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
38
- // Copy/pasted from StaticField, maybe we should combine?
39
- ...Css_1.Css.sm.gray900.df.aic.mh((0, Css_1.px)(40)).$,
40
- ...Css_1.Css.maxw((0, Css_1.px)(500)).$,
41
- ...(multiline ? Css_1.Css.fdc.aifs.childGap2.$ : Css_1.Css.add({ overflow: "hidden", whiteSpace: "nowrap" }).$),
68
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: fieldStyles.container }, groupProps, focusWithinProps, { children: [label && !inlineLabel && ((0, jsx_runtime_1.jsx)(Label_1.Label, Object.assign({ labelProps: labelProps, hidden: hideLabel, label: label, suffix: labelSuffix, contrast: contrast }, tid.label), void 0)), readOnly && ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
69
+ // Use input wrapper to get common styles, but then we need to override some
70
+ ...fieldStyles.inputWrapperReadOnly,
71
+ ...(multiline ? Css_1.Css.fdc.aifs.childGap2.$ : Css_1.Css.truncate.$),
42
72
  ...xss,
43
- } }, tid, { "data-readonly": "true" }, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), multiline
73
+ }, "data-readonly": "true" }, tid, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), multiline
44
74
  ? (_a = inputProps.value) === null || _a === void 0 ? void 0 : _a.split("\n\n").map((p, i) => ((0, jsx_runtime_1.jsx)("p", Object.assign({ css: Css_1.Css.my1.$ }, { children: p.split("\n").map((sentence, j) => ((0, jsx_runtime_1.jsxs)("span", { children: [sentence, (0, jsx_runtime_1.jsx)("br", {}, void 0)] }, j))) }), i)))
45
75
  : inputProps.value] }), void 0)), !readOnly && ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
46
- ...Css_1.Css.df.aic.bgWhite.sm.px1.w100.hPx(40).gray900.br4.ba.bGray300.overflowHidden.if(compact).hPx(32).$,
47
- ...(inputProps.disabled ? Css_1.Css.gray400.bgGray100.cursorNotAllowed.$ : {}),
48
- ...(isFocused ? Css_1.Css.bLightBlue700.$ : {}),
49
- ...(errorMsg ? Css_1.Css.bRed600.$ : {}),
76
+ ...fieldStyles.inputWrapper,
77
+ ...(inputProps.disabled ? fieldStyles.disabled : {}),
78
+ ...(isFocused && !readOnly ? fieldStyles.focus : {}),
79
+ ...(isHovered && !inputProps.disabled && !readOnly && !isFocused ? fieldStyles.hover : {}),
80
+ ...(errorMsg ? fieldStyles.error : {}),
50
81
  ...Css_1.Css.if(multiline).aifs.px0.mh((0, Css_1.px)(96)).$,
51
- }, ref: inputWrapRef }, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), (0, jsx_runtime_1.jsx)(ElementType, Object.assign({}, (0, react_aria_1.mergeProps)(inputProps, { onBlur, onFocus: onFocusChained, onChange: onDomChange }, { "aria-invalid": Boolean(errorMsg), ...(hideLabel ? { "aria-label": label } : {}) }), (errorMsg ? { "aria-errormessage": errorMessageId } : {}), { ref: inputRef, rows: multiline ? 1 : undefined, css: {
52
- ...Css_1.Css.add("resize", "none").sm.w100.gray900.outline0.$,
53
- ...(inputProps.disabled ? Css_1.Css.gray400.bgGray100.cursorNotAllowed.$ : {}),
82
+ } }, hoverProps, { ref: inputWrapRef }, { children: [!multiline && inlineLabel && label && !hideLabel && ((0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0)), !multiline && startAdornment && (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.df.aic.fs0.br4.pr1.$ }, { children: startAdornment }), void 0), (0, jsx_runtime_1.jsx)(ElementType, Object.assign({}, (0, react_aria_1.mergeProps)(inputProps, { onBlur, onFocus: onFocusChained, onChange: onDomChange }, { "aria-invalid": Boolean(errorMsg), ...(hideLabel ? { "aria-label": label } : {}) }), (errorMsg ? { "aria-errormessage": errorMessageId } : {}), { ref: inputRef, rows: multiline ? 1 : undefined, css: {
83
+ ...fieldStyles.input,
84
+ ...(inputProps.disabled ? fieldStyles.disabled : {}),
85
+ ...(isHovered && !inputProps.disabled && !readOnly && !isFocused ? fieldStyles.hover : {}),
86
+ ...(multiline ? Css_1.Css.h100.p1.add("resize", "none").$ : Css_1.Css.truncate.$),
54
87
  ...xss,
55
- ...Css_1.Css.if(multiline).h100.p1.$,
56
- } }, tid), void 0), !multiline && endAdornment && (0, jsx_runtime_1.jsx)("span", { children: endAdornment }, void 0)] }), void 0)), errorMsg && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, Object.assign({ id: errorMessageId, errorMsg: errorMsg }, tid.errorMsg), void 0), helperText && (0, jsx_runtime_1.jsx)(HelperText_1.HelperText, Object.assign({ helperText: helperText }, tid.helperText), void 0)] }), void 0));
88
+ } }, tid), void 0), !multiline && endAdornment && (0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.df.aic.pl1.fs0.$ }, { children: endAdornment }), void 0)] }), void 0)), errorMsg && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, Object.assign({ id: errorMessageId, errorMsg: errorMsg }, tid.errorMsg), void 0), helperText && (0, jsx_runtime_1.jsx)(HelperText_1.HelperText, Object.assign({ helperText: helperText }, tid.helperText), void 0)] }), void 0));
57
89
  }
58
90
  exports.TextFieldBase = TextFieldBase;
@@ -32,7 +32,7 @@ export interface BeamSelectFieldBaseProps<T, V extends Value> extends BeamFocusa
32
32
  /** Allow placing an icon/decoration within the input field. */
33
33
  fieldDecoration?: (opt: T) => ReactNode;
34
34
  /** Sets the form field label. */
35
- label?: string;
35
+ label: string;
36
36
  hideLabel?: boolean;
37
37
  /** Renders the label inside the input field, i.e. for filters. */
38
38
  inlineLabel?: boolean;
@@ -44,4 +44,6 @@ export interface BeamSelectFieldBaseProps<T, V extends Value> extends BeamFocusa
44
44
  nothingSelectedText?: string;
45
45
  /** When set the SelectField is expected to be put on a darker background */
46
46
  contrast?: boolean;
47
+ /** If the field should be rendered without a border - This could happen if rendering within a table or as part of a CompoundField */
48
+ borderless?: boolean;
47
49
  }
@@ -21,7 +21,7 @@ const Value_1 = require("../Value");
21
21
  */
22
22
  function SelectFieldBase(props) {
23
23
  var _a;
24
- const { compact = false, disabled: isDisabled = false, errorMsg, helperText, label, hideLabel, required, inlineLabel, readOnly: isReadOnly = false, onSelect, fieldDecoration, options, onBlur, onFocus, multiselect = false, getOptionLabel, getOptionValue, getOptionMenuLabel = getOptionLabel, sizeToContent = false, values, nothingSelectedText = "", contrast, disabledOptions, ...otherProps } = props;
24
+ const { compact = false, disabled: isDisabled = false, errorMsg, helperText, label, hideLabel, required, inlineLabel, readOnly: isReadOnly = false, onSelect, fieldDecoration, options, onBlur, onFocus, multiselect = false, getOptionLabel, getOptionValue, getOptionMenuLabel = getOptionLabel, sizeToContent = false, values, nothingSelectedText = "", contrast, disabledOptions, borderless, ...otherProps } = props;
25
25
  const { contains } = (0, react_aria_1.useFilter)({ sensitivity: "base" });
26
26
  function onSelectionChange(keys) {
27
27
  // Close menu upon selection change only for Single selection mode
@@ -177,6 +177,6 @@ function SelectFieldBase(props) {
177
177
  ...positionProps.style,
178
178
  width: (_a = comboBoxRef === null || comboBoxRef === void 0 ? void 0 : comboBoxRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth,
179
179
  };
180
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$, ref: comboBoxRef }, { children: [(0, jsx_runtime_1.jsx)(SelectFieldInput_1.SelectFieldInput, { buttonProps: buttonProps, buttonRef: triggerRef, compact: compact, errorMsg: errorMsg, helperText: helperText, fieldDecoration: fieldDecoration, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, isDisabled: isDisabled, required: required, isReadOnly: isReadOnly, state: state, onBlur: onBlur, onFocus: onFocus, inlineLabel: inlineLabel, label: label, hideLabel: hideLabel, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, sizeToContent: sizeToContent, contrast: contrast, nothingSelectedText: nothingSelectedText }, void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, listBoxProps, { positionProps: positionProps, state: state, compact: compact, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast }), void 0) }), void 0))] }), void 0));
180
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$, ref: comboBoxRef }, { children: [(0, jsx_runtime_1.jsx)(SelectFieldInput_1.SelectFieldInput, Object.assign({}, otherProps, { buttonProps: buttonProps, buttonRef: triggerRef, compact: compact, errorMsg: errorMsg, helperText: helperText, fieldDecoration: fieldDecoration, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, isDisabled: isDisabled, required: required, isReadOnly: isReadOnly, state: state, onBlur: onBlur, onFocus: onFocus, inlineLabel: inlineLabel, label: label, hideLabel: hideLabel, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, sizeToContent: sizeToContent, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, listBoxProps, { positionProps: positionProps, state: state, compact: compact, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast }), void 0) }), void 0))] }), void 0));
181
181
  }
182
182
  exports.SelectFieldBase = SelectFieldBase;
@@ -19,7 +19,7 @@ interface SelectFieldInputProps<O, V extends Value> {
19
19
  onFocus?: () => void;
20
20
  inlineLabel?: boolean;
21
21
  labelProps: LabelHTMLAttributes<HTMLLabelElement>;
22
- label?: string;
22
+ label: string;
23
23
  hideLabel?: boolean;
24
24
  selectedOptions: O[];
25
25
  getOptionValue: (opt: O) => V;
@@ -27,6 +27,8 @@ interface SelectFieldInputProps<O, V extends Value> {
27
27
  sizeToContent: boolean;
28
28
  contrast?: boolean;
29
29
  nothingSelectedText: string;
30
+ /** If the field should be rendered without a border - This could happen if rendering within a table or as part of a CompoundField */
31
+ borderless?: boolean;
30
32
  }
31
33
  export declare function SelectFieldInput<O, V extends Value>(props: SelectFieldInputProps<O, V>): import("@emotion/react/jsx-runtime").JSX.Element;
32
34
  export {};
@@ -5,121 +5,94 @@ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const react_aria_1 = require("react-aria");
7
7
  const components_1 = require("../../components");
8
- const HelperText_1 = require("../../components/HelperText");
9
- const Label_1 = require("../../components/Label");
10
8
  const Css_1 = require("../../Css");
11
- const labelUtils_1 = require("../../forms/labelUtils");
12
- const ErrorMessage_1 = require("../ErrorMessage");
9
+ const TextFieldBase_1 = require("../TextFieldBase");
13
10
  const Value_1 = require("../Value");
14
11
  const utils_1 = require("../../utils");
15
12
  function SelectFieldInput(props) {
16
- const { inputProps, inputRef, inputWrapRef, buttonProps, buttonRef, compact = false, errorMsg, required, helperText, state, fieldDecoration, isDisabled, isReadOnly, onBlur, onFocus, inlineLabel, label, labelProps, hideLabel, selectedOptions, getOptionValue, getOptionLabel, sizeToContent, contrast = false, nothingSelectedText, } = props;
17
- const themeStyles = {
18
- wrapper: Css_1.Css.bgWhite.bGray300.gray900.if(contrast).bgGray700.bGray700.white.$,
19
- hover: Css_1.Css.bgGray100.if(contrast).bgGray600.bGray600.$,
20
- focus: Css_1.Css.bLightBlue700.if(contrast).bLightBlue500.$,
21
- // Not using Truss's inline `if` statement here because `addIn` properties are applied regardless.
22
- input: !contrast ? Css_1.Css.bgWhite.$ : Css_1.Css.bgGray700.addIn("&::selection", Css_1.Css.bgGray800.$).$,
23
- disabled: Css_1.Css.cursorNotAllowed.gray400.bgGray100.if(contrast).gray500.bgGray700.$,
24
- error: Css_1.Css.bRed500.if(contrast).bRed400.$,
25
- };
26
- const errorMessageId = `${inputProps.id}-error`;
13
+ const { inputProps, inputRef, inputWrapRef, buttonProps, buttonRef, compact = false, errorMsg, required, helperText, state, fieldDecoration, isDisabled, isReadOnly, onBlur, onFocus, inlineLabel, label, labelProps, hideLabel, selectedOptions, getOptionValue, getOptionLabel, sizeToContent, contrast = false, nothingSelectedText, borderless, ...otherProps } = props;
27
14
  const [isFocused, setIsFocused] = (0, react_1.useState)(false);
28
- const { hoverProps, isHovered } = (0, react_aria_1.useHover)({});
29
- const hoverStyles = isHovered && !isReadOnly && !isFocused ? themeStyles.hover : {};
30
- const focusStyles = isFocused && !isReadOnly ? themeStyles.focus : {};
31
- const errorStyles = errorMsg ? themeStyles.error : {};
32
- const disabledStyles = isDisabled ? themeStyles.disabled : {};
33
- const readOnlyStyles = isReadOnly ? Css_1.Css.bn.pl0.pt0.add("backgroundColor", "unset").$ : {};
34
- const tid = (0, utils_1.useTestIds)(inputProps); // data-testid comes in through here
35
15
  const isMultiSelect = state.selectionManager.selectionMode === "multiple";
36
- const labelSuffix = (0, labelUtils_1.getLabelSuffix)(required);
37
- return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [!inlineLabel && label && ((0, jsx_runtime_1.jsx)(Label_1.Label, Object.assign({ labelProps: labelProps, label: label, suffix: labelSuffix, contrast: contrast }, tid.label, { hidden: hideLabel }), void 0)), (0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
38
- ...Css_1.Css.df.ba.br4.px1.aic.hPx(40).if(compact).hPx(32).$,
39
- ...themeStyles.wrapper,
40
- ...hoverStyles,
41
- ...errorStyles,
42
- ...focusStyles,
43
- ...disabledStyles,
44
- ...readOnlyStyles,
45
- } }, hoverProps, { ref: inputWrapRef }, { children: [inlineLabel && label && (0, jsx_runtime_1.jsx)(Label_1.InlineLabel, Object.assign({ labelProps: labelProps, label: label }, tid.label), void 0), isMultiSelect && state.selectionManager.selectedKeys.size > 1 && ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.wPx(16).hPx(16).mr1.fs0.br100.bgLightBlue700.white.tinyEm.df.aic.jcc.$ }, { children: state.selectionManager.selectedKeys.size }), void 0)), (!isMultiSelect || (isMultiSelect && !isFocused)) && fieldDecoration && selectedOptions.length === 1 && ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: {
46
- ...Css_1.Css.df.aic.br4.fs0.pr1.$,
47
- ...errorStyles,
48
- ...hoverStyles,
49
- } }, { children: fieldDecoration(selectedOptions[0]) }), void 0)), (0, jsx_runtime_1.jsx)("input", Object.assign({}, (0, react_aria_1.mergeProps)(inputProps, { "aria-invalid": Boolean(errorMsg), onInput: () => state.open() }), (errorMsg ? { "aria-errormessage": errorMessageId } : {}), { ref: inputRef, css: {
50
- ...Css_1.Css.mw0.fg1.pr1.br4.outline0.truncate.w100.sm.if(!inlineLabel).smEm.$,
51
- ...themeStyles.input,
52
- ...hoverStyles,
53
- ...disabledStyles,
54
- ...readOnlyStyles,
55
- }, onKeyDown: (e) => {
56
- // We need to do some custom logic when using MultiSelect, as react-aria/stately Combobox doesn't support multiselect out of the box.
57
- if (isMultiSelect) {
58
- // Enter should toggle the focused item.
59
- if (e.key === "Enter") {
60
- // Prevent form submissions if menu is open.
61
- if (state.isOpen) {
62
- e.preventDefault();
63
- }
64
- state.selectionManager.toggleSelection(state.selectionManager.focusedKey);
65
- return;
66
- }
67
- // By default, the Escape key would "revert" changes,
68
- // but we just want to close the menu and leave the selections as is
69
- if (e.key === "Escape") {
70
- state.close();
71
- return;
72
- }
16
+ const showNumSelection = isMultiSelect && state.selectionManager.selectedKeys.size > 1;
17
+ // For MultiSelect only show the `fieldDecoration` when input is not in focus.
18
+ const showFieldDecoration = (!isMultiSelect || (isMultiSelect && !isFocused)) && fieldDecoration && selectedOptions.length === 1;
19
+ return ((0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({}, otherProps, { inputRef: inputRef, inputWrapRef: inputWrapRef, label: label, readOnly: isReadOnly, hideLabel: hideLabel, labelProps: labelProps, inlineLabel: inlineLabel, compact: compact, required: required, errorMsg: errorMsg, helperText: helperText, contrast: contrast, xss: !inlineLabel ? Css_1.Css.fw5.$ : {}, borderless: borderless, startAdornment: (showNumSelection && ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.wPx(16).hPx(16).fs0.br100.bgLightBlue700.white.tinyEm.df.aic.jcc.$ }, { children: state.selectionManager.selectedKeys.size }), void 0))) ||
20
+ (showFieldDecoration && fieldDecoration(selectedOptions[0])), endAdornment: !isReadOnly && ((0, jsx_runtime_1.jsx)("button", Object.assign({}, buttonProps, { disabled: isDisabled, ref: buttonRef, css: {
21
+ ...Css_1.Css.br4.outline0.gray700.if(contrast).gray400.$,
22
+ ...(isDisabled ? Css_1.Css.cursorNotAllowed.gray400.if(contrast).gray600.$ : {}),
23
+ } }, { children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: state.isOpen ? "chevronUp" : "chevronDown" }, void 0) }), void 0)), inputProps: {
24
+ ...(0, react_aria_1.mergeProps)(inputProps, { "aria-invalid": Boolean(errorMsg), onInput: () => state.open() }),
25
+ // Not merging the following as we want them to overwrite existing events
26
+ ...{
27
+ onKeyDown: (e) => {
28
+ // We need to do some custom logic when using MultiSelect, as react-aria/stately Combobox doesn't support multiselect out of the box.
29
+ if (isMultiSelect) {
30
+ // Enter should toggle the focused item.
31
+ if (e.key === "Enter") {
32
+ // Prevent form submissions if menu is open.
33
+ if (state.isOpen) {
34
+ e.preventDefault();
73
35
  }
74
- // Handle single selection Escape key press
75
- // When a user hits `Escape`, then react-aria calls `state.revert`, which uses `state.selectedKey` to
76
- // reset the field to its previous value. However, because we use a the Multiple Selection State manager,
77
- // then our `state.selectedKey` isn't set. So we need to properly reset the state ourselves.
78
- if (e.key === "Escape") {
79
- // Triggering `Escape` is basically like re-selecting currently selected option, so do that if there is one.
80
- state.selectionManager.setSelectedKeys(selectedOptions.length > 0 ? [(0, Value_1.valueToKey)(getOptionValue(selectedOptions[0]))] : []);
81
- return;
82
- }
83
- inputProps.onKeyDown && inputProps.onKeyDown(e);
84
- }, onBlur: () => {
85
- // We purposefully override onBlur here instead of using mergeProps, b/c inputProps.onBlur
86
- // goes into useComboBox's onBlur, which calls setFocused(false), which in useComboBoxState
87
- // detects a) there is no props.selectedKey (b/c we don't pass it), and b) there is an
88
- // `inputValue`, so it thinks it needs to call `resetInputValue()`.
89
- //
90
- // I assume we don't pass `selectedKey` b/c we support multiple keys.
91
- if (isReadOnly) {
92
- return;
93
- }
94
- (0, utils_1.maybeCall)(onBlur);
36
+ state.selectionManager.toggleSelection(state.selectionManager.focusedKey);
37
+ return;
38
+ }
39
+ // By default, the Escape key would "revert" changes,
40
+ // but we just want to close the menu and leave the selections as is
41
+ if (e.key === "Escape") {
95
42
  state.close();
96
- setIsFocused(false);
97
- // Always call `setSelectedKeys` onBlur with its existing selected keys..
98
- // This ensures the field's `input.value` resets to what it should be in case it doesn't currently match.
99
- state.selectionManager.setSelectedKeys(state.selectionManager.selectedKeys);
100
- }, onFocus: (e) => {
101
- if (isReadOnly)
102
- return;
103
- (0, utils_1.maybeCall)(onFocus);
104
- e.target.select();
105
- state.open();
106
- setIsFocused(true);
107
- }, size:
108
- // If sizeToContent, then, in order of precedence, base it of from:
109
- // 1. input's value if any
110
- // 2. If is MultiSelect and only one option is chosen, then use the length of that option to define the width to avoid size jumping on blur.
111
- // 3. Use `nothingSelectedText`
112
- // 4. Default to "1"
113
- // And do not allow it to grow past a size of 20.
114
- // TODO: Combine logic to determine the input's value. Similar logic is used in SelectFieldBase, though it is intertwined with other state logic. Such as when to open/close menu, or filter the options within that menu, etc...
115
- sizeToContent
116
- ? Math.min(String(inputProps.value ||
117
- (isMultiSelect && selectedOptions.length === 1 && getOptionLabel(selectedOptions[0])) ||
118
- nothingSelectedText ||
119
- "").length || 1, 20)
120
- : undefined }), void 0), !isReadOnly && ((0, jsx_runtime_1.jsx)("button", Object.assign({}, buttonProps, { disabled: isDisabled, ref: buttonRef, css: {
121
- ...Css_1.Css.br4.outline0.$,
122
- ...disabledStyles,
123
- } }, { children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: state.isOpen ? "chevronUp" : "chevronDown", color: contrast ? Css_1.Palette.Gray400 : Css_1.Palette.Gray700 }, void 0) }), void 0))] }), void 0), errorMsg && (0, jsx_runtime_1.jsx)(ErrorMessage_1.ErrorMessage, Object.assign({ id: errorMessageId, errorMsg: errorMsg, contrast: contrast }, tid.errorMsg), void 0), helperText && (0, jsx_runtime_1.jsx)(HelperText_1.HelperText, Object.assign({ helperText: helperText, contrast: contrast }, tid.helperText), void 0)] }, void 0));
43
+ return;
44
+ }
45
+ }
46
+ // Handle single selection Escape key press
47
+ // When a user hits `Escape`, then react-aria calls `state.revert`, which uses `state.selectedKey` to
48
+ // reset the field to its previous value. However, because we use a the Multiple Selection State manager,
49
+ // then our `state.selectedKey` isn't set. So we need to properly reset the state ourselves.
50
+ if (e.key === "Escape") {
51
+ // Triggering `Escape` is basically like re-selecting currently selected option, so do that if there is one.
52
+ state.selectionManager.setSelectedKeys(selectedOptions.length > 0 ? [(0, Value_1.valueToKey)(getOptionValue(selectedOptions[0]))] : []);
53
+ return;
54
+ }
55
+ inputProps.onKeyDown && inputProps.onKeyDown(e);
56
+ },
57
+ onBlur: () => {
58
+ // We purposefully override onBlur here instead of using mergeProps, b/c inputProps.onBlur
59
+ // goes into useComboBox's onBlur, which calls setFocused(false), which in useComboBoxState
60
+ // detects a) there is no props.selectedKey (b/c we don't pass it), and b) there is an
61
+ // `inputValue`, so it thinks it needs to call `resetInputValue()`.
62
+ //
63
+ // I assume we don't pass `selectedKey` b/c we support multiple keys.
64
+ if (isReadOnly) {
65
+ return;
66
+ }
67
+ setIsFocused(false);
68
+ (0, utils_1.maybeCall)(onBlur);
69
+ state.close();
70
+ // Always call `setSelectedKeys` onBlur with its existing selected keys..
71
+ // This ensures the field's `input.value` resets to what it should be in case it doesn't currently match.
72
+ state.selectionManager.setSelectedKeys(state.selectionManager.selectedKeys);
73
+ },
74
+ onFocus: () => {
75
+ if (isReadOnly)
76
+ return;
77
+ setIsFocused(true);
78
+ (0, utils_1.maybeCall)(onFocus);
79
+ state.open();
80
+ },
81
+ size:
82
+ // If sizeToContent, then, in order of precedence, base it of from:
83
+ // 1. input's value if any
84
+ // 2. If is MultiSelect and only one option is chosen, then use the length of that option to define the width to avoid size jumping on blur.
85
+ // 3. Use `nothingSelectedText`
86
+ // 4. Default to "1"
87
+ // And do not allow it to grow past a size of 20.
88
+ // TODO: Combine logic to determine the input's value. Similar logic is used in SelectFieldBase, though it is intertwined with other state logic. Such as when to open/close menu, or filter the options within that menu, etc...
89
+ sizeToContent
90
+ ? Math.min(String(inputProps.value ||
91
+ (isMultiSelect && selectedOptions.length === 1 && getOptionLabel(selectedOptions[0])) ||
92
+ nothingSelectedText ||
93
+ "").length || 1, 20)
94
+ : undefined,
95
+ },
96
+ } }), void 0));
124
97
  }
125
98
  exports.SelectFieldInput = SelectFieldInput;
@@ -38,4 +38,6 @@ export interface BeamTextFieldProps extends BeamFocusableProps {
38
38
  onFocus?: () => void;
39
39
  readOnly?: boolean;
40
40
  placeholder?: string;
41
+ /** If the field should be rendered without a border - This could happen if rendering within a table or as part of a CompoundField */
42
+ borderless?: boolean;
41
43
  }
package/dist/types.d.ts CHANGED
@@ -5,3 +5,8 @@ export declare type HasIdAndName<V = string> = {
5
5
  export declare type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
6
6
  export declare type Callback = () => void;
7
7
  export declare type CheckFn = () => boolean;
8
+ export declare type CanCloseCheck = {
9
+ check: CheckFn;
10
+ discardText?: string;
11
+ continueText?: string;
12
+ } | CheckFn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.78.2",
3
+ "version": "2.79.2",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",