Skip to content
Draft
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
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"cSpell.words": ["boxel"],
"editor.formatOnSave": true,
"files.associations": {
"*.gts": "glimmer-ts",
"*.gjs": "glimmer-js"
},
"[glimmer-js]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
Expand Down
2 changes: 2 additions & 0 deletions packages/boxel-ui/addon/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
cardsInColumn,
columnCount as kanbanColumnCount,
findInsertionFromPointer,
KanbanColumnConfigSidebar,
KanbanDragManager,
KanbanPlane,
resolveInsertion,
Expand Down Expand Up @@ -138,6 +139,7 @@ export {
GridContainer,
Header,
IconButton,
KanbanColumnConfigSidebar,
kanbanColumnCount,
KanbanDragManager,
KanbanPlane,
Expand Down
29 changes: 29 additions & 0 deletions packages/boxel-ui/addon/src/components/field-container/index.gts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface Signature {
icon?: Icon;
iconHeight?: string;
iconWidth?: string;
inline?: boolean;
label: string;
labelFontSize?: BoxelLabelFontSize;
tag?: keyof HTMLElementTagNameMap;
Expand All @@ -33,6 +34,7 @@ const FieldContainer: TemplateOnlyComponent<Signature> = <template>
'boxel-field'
vertical=(or @vertical @centeredDisplay)
horizontal=(not (or @vertical @centeredDisplay))
inline=@inline
small-label=(eq @horizontalLabelSize 'small')
centered-display=@centeredDisplay
with-icon=(bool @icon)
Expand Down Expand Up @@ -111,6 +113,9 @@ const FieldContainer: TemplateOnlyComponent<Signature> = <template>
); /* necessary for our various overlays utilizing box-shadow */
word-break: break-word;
}
.content:not(:has(input)) {
text-box-trim: trim-both;
}

.horizontal {
grid-template-columns:
Expand All @@ -127,6 +132,30 @@ const FieldContainer: TemplateOnlyComponent<Signature> = <template>
align-self: center;
}

.horizontal.inline {
--boxel-field-label-size: auto;
--boxel-field-content-size: auto;
display: inline-grid;
grid-template-columns:
var(--boxel-field-label-size)
var(--boxel-field-content-size);
width: fit-content;
min-height: unset;
gap: var(--boxel-sp-2xs);
}

.horizontal.inline > .label-container {
padding-top: 0;
align-self: start;
display: flex;
align-items: center;
height: var(--boxel-form-control-height);
}

.horizontal.inline > .content {
overflow: clip;
}

.vertical {
grid-template-rows: auto 1fr;
gap: var(--boxel-sp-4xs);
Expand Down
89 changes: 76 additions & 13 deletions packages/boxel-ui/addon/src/components/field-container/usage.gts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@ export default class FieldUsage extends Component {
@tracked icon = Profile;
@tracked tag?: keyof HTMLElementTagNameMap;

@tracked vertical2 = false;
@tracked horizontalLabelSize2 = 'default';
@tracked icon2: Icon | undefined;
@tracked inline = false;

@tracked inline2 = false;

@tracked inline3 = false;
@tracked vertical3 = false;
@tracked horizontalLabelSize3 = 'default';
@tracked icon3: Icon | undefined;
@cssVariable({ cssClassName: 'boxel-field' })
declare boxelFieldLabelAlign: CSSVariableInfo;
@cssVariable({ cssClassName: 'boxel-field' })
Expand All @@ -42,6 +47,7 @@ export default class FieldUsage extends Component {
@label={{this.label}}
@fieldId={{this.id}}
@vertical={{this.vertical}}
@inline={{this.inline}}
@horizontalLabelSize={{this.horizontalLabelSize}}
@labelFontSize={{this.labelFontSize}}
@centeredDisplay={{this.centeredDisplay}}
Expand Down Expand Up @@ -89,6 +95,13 @@ export default class FieldUsage extends Component {
@onInput={{fn (mut this.vertical)}}
@value={{this.vertical}}
/>
<Args.Bool
@name='inline'
@description='Compact horizontal layout: label column shrinks to content width, min-height removed. Use when embedding the field inside a flex row alongside other controls.'
@defaultValue='false'
@onInput={{fn (mut this.inline)}}
@value={{this.inline}}
/>
<Args.String
@name='horizontalLabelSize'
@description='Width of the label column (only applies to horizontal layout)'
Expand Down Expand Up @@ -135,20 +148,63 @@ export default class FieldUsage extends Component {

<FreestyleUsage @name='Usage with Boxel::Input'>
<:example>
<BoxelFieldContainer @tag='label' @label='Name'>
<BoxelFieldContainer
@tag='label'
@label='Name'
@inline={{this.inline2}}
>
<BoxelInput @id='usage-boxel-input' @value='' />
</BoxelFieldContainer>
</:example>
<:api as |Args|>
<Args.Bool
@name='inline'
@description='Compact horizontal layout: label column shrinks to content width, min-height removed.'
@defaultValue='false'
@onInput={{fn (mut this.inline2)}}
@value={{this.inline2}}
/>
</:api>
</FreestyleUsage>

<FreestyleUsage @name='Inline (compact horizontal)'>
<:description>
Use
<code>@inline</code>
when placing a labeled control inside a flex row alongside other
elements. The label column shrinks to fit its text and
<code>min-height</code>
is removed so the field doesn't impose extra height on the row.
</:description>
<:example>
<div class='inline-example-row'>
<BoxelFieldContainer @tag='label' @label='Max' @inline={{true}}>
<BoxelInput @type='number' @value={{0}} @min={{0}} />
</BoxelFieldContainer>
<BoxelFieldContainer @tag='label' @label='Min' @inline={{true}}>
<BoxelInput @type='number' @value={{0}} @min={{0}} />
</BoxelFieldContainer>
</div>
</:example>
</FreestyleUsage>

<style scoped>
.inline-example-row {
display: flex;
align-items: center;
gap: var(--boxel-sp-sm);
}
</style>

<FreestyleUsage @name='Usage with Boxel::Input (invalid state)'>
<:example>
<BoxelFieldContainer
@tag='label'
@label='Name'
@vertical={{this.vertical2}}
@horizontalLabelSize={{this.horizontalLabelSize2}}
@icon={{this.icon2}}
@inline={{this.inline3}}
@vertical={{this.vertical3}}
@horizontalLabelSize={{this.horizontalLabelSize3}}
@icon={{this.icon3}}
>
<BoxelInput
@id=''
Expand All @@ -163,24 +219,31 @@ export default class FieldUsage extends Component {
<Args.Component
@name='icon'
@description='icon component reference'
@value={{this.icon2}}
@value={{this.icon3}}
@options={{ALL_ICON_COMPONENTS}}
@onChange={{fn (mut this.icon2)}}
@onChange={{fn (mut this.icon3)}}
/>
<Args.Bool
@name='inline'
@description='Compact horizontal layout: label column shrinks to content width, min-height removed.'
@defaultValue='false'
@onInput={{fn (mut this.inline3)}}
@value={{this.inline3}}
/>
<Args.Bool
@name='vertical'
@description='Whether the field should be displayed vertically'
@defaultValue='false'
@onInput={{fn (mut this.vertical2)}}
@value={{this.vertical2}}
@onInput={{fn (mut this.vertical3)}}
@value={{this.vertical3}}
/>
<Args.String
@name='horizontalLabelSize'
@description='Width of the label column (only applies to horizontal layout)'
@options={{array 'small' 'default'}}
@defaultValue='minmax(4rem, 25%)'
@onInput={{fn (mut this.horizontalLabelSize2)}}
@value={{this.horizontalLabelSize2}}
@onInput={{fn (mut this.horizontalLabelSize3)}}
@value={{this.horizontalLabelSize3}}
/>
</:api>
</FreestyleUsage>
Expand Down
56 changes: 49 additions & 7 deletions packages/boxel-ui/addon/src/components/input/index.gts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export const InputValidationStates = {

export type InputValidationState = Values<typeof InputValidationStates>;

export const InputSizes = {
Default: 'default',
Large: 'large',
} as const;

export const InputBottomTreatments = {
Flat: 'flat',
Rounded: 'rounded',
Expand All @@ -74,7 +79,7 @@ export interface Signature {
placeholder?: string;
readonly?: boolean;
required?: boolean;
size?: 'large' | 'default';
size?: Values<typeof InputSizes>;
state?: InputValidationState;
type?: InputType;
value: string | number | boolean | null | undefined;
Expand Down Expand Up @@ -248,14 +253,16 @@ export default class BoxelInput extends Component<Signature> {
.input-container {
--icon-size: var(--boxel-icon-sm);
--icon-space: var(--boxel-sp-xs);
--icon-full-length: calc(
var(--boxel-icon-sm) + var(--boxel-sp-xs) * 2
--_icon-full-length: var(
--boxel-input-icon-size,
calc(var(--icon-size) + var(--icon-space) * 2)
);

display: grid;
grid-template-columns: var(--icon-full-length) 1fr var(
--icon-full-length
);
grid-template-columns:
var(--_icon-full-length)
1fr
var(--_icon-full-length);
grid-template-areas:
'optional optional optional'
'pre-icon input post-icon'
Expand All @@ -269,7 +276,8 @@ export default class BoxelInput extends Component<Signature> {
grid-row: 2;

box-sizing: border-box;
width: 100%;
width: var(--boxel-input-width, 100%);
min-width: 0;
max-width: 100%;
min-height: var(
--boxel-input-height,
Expand Down Expand Up @@ -299,6 +307,35 @@ export default class BoxelInput extends Component<Signature> {
overflow: auto;
}

.input-container:has([type='color']) {
--boxel-input-icon-size: 0;
}

.input-container:has([type='color']) .validation-icon-container,
.input-container:has([type='color']) .error-message,
.input-container:has([type='color']) .helper-text {
display: none;
}

.boxel-input[type='color'] {
width: 1.5rem;
height: 1.5rem;
min-height: unset;
padding: 0;
background: none;
border-radius: var(--boxel-border-radius-sm);
cursor: pointer;
}

.boxel-input[type='color']::-webkit-color-swatch-wrapper {
padding: 0;
}

.boxel-input[type='color']::-webkit-color-swatch {
border: none;
border-radius: calc(var(--boxel-border-radius-sm) - 1px);
}

.boxel-input:not([type='color']):disabled {
opacity: 0.5;
resize: none; /* do not display resize toggle since it's disabled */
Expand Down Expand Up @@ -392,6 +429,11 @@ export default class BoxelInput extends Component<Signature> {
user-select: none;
}

.is-multiline .validation-icon-container {
align-items: flex-start;
padding-top: var(--boxel-sp-xs);
}

.search ~ .validation-icon-container .validation-icon-loading {
color: var(--primary, var(--boxel-highlight));
--icon-color: currentColor;
Expand Down
9 changes: 6 additions & 3 deletions packages/boxel-ui/addon/src/components/input/usage.gts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import BoxelInput from './index.gts';
import {
type InputValidationState,
InputBottomTreatments,
InputSizes,
InputTypes,
InputValidationStates,
} from './index.gts';

const validTypes = Object.values(InputTypes);
const validBottomTreatments = Object.values(InputBottomTreatments);
const validStates = Object.values(InputValidationStates);
const validSizes = Object.values(InputSizes);

export default class InputUsage extends Component {
@tracked id = 'sample-input';
Expand All @@ -37,7 +39,8 @@ export default class InputUsage extends Component {
@tracked min = '';
@tracked max = '';
@tracked state: InputValidationState = 'initial';
@tracked size: 'large' | 'default' = 'default';
@tracked size: (typeof InputSizes)[keyof typeof InputSizes] =
InputSizes.Default;

@tracked isChecked = false;

Expand Down Expand Up @@ -196,9 +199,9 @@ export default class InputUsage extends Component {
/>
<Args.String
@name='size'
@description='Optional larger size'
@description='Input size: large increases height, auto sets container width to fit-content'
@onInput={{fn (mut this.size)}}
@options={{Array 'default' 'large'}}
@options={{validSizes}}
@value={{this.size}}
@defaultValue={{this.size}}
/>
Expand Down
Loading
Loading