403 lines
11 KiB
Text
403 lines
11 KiB
Text
/* Form layout */
|
|
|
|
@import "./variables.less";
|
|
|
|
:root {
|
|
// CSS variable to define the number of (label + control) columns.
|
|
// It dynamically changes on narrow pages (see media query below).
|
|
--ct-form-columns: 2;
|
|
}
|
|
|
|
// Cockpit Form Layout: Automatically have Cockpit display your form in
|
|
// an optimal layout.
|
|
//
|
|
// By default, all labels are aligned and sized properly and form elements
|
|
// stretch to take up the remaining space.
|
|
//
|
|
//
|
|
// There are additional classes and attributes you can add to each
|
|
// control directly under `ct-form`:
|
|
//
|
|
// `ct-form-split`: The grid can be split on a `form-control`
|
|
// level by adding a this class. If you want two elements next to each
|
|
// other, both should have this class. Widths are equal by default.
|
|
// See ct-form-minmax & ct-form-maxmin for alternate sizing.
|
|
//
|
|
// `ct-form-relax`: Form elements normally stretch to take up the
|
|
// full space. You can relax their width by adding this class to the
|
|
// control. Inputs with a size attribute are auto-relaxed and do not
|
|
// need this class.
|
|
//
|
|
// `ct-form-stretch`: If a control has a width specified
|
|
// elsewhere, you can force it to stretch. This is mainly useful when
|
|
// using <div role="group"> to group elements.
|
|
//
|
|
// `ct-form-full`: Force a widget to be the full width of the form,
|
|
// invading the label space.
|
|
//
|
|
// role="group": When there are two related elements, such as a text
|
|
// input and a dropdown, you can group them together using this HTML
|
|
// attribute. It's similar in purpose to a <fieldset>, but works for
|
|
// layouts in Chrome (unlike fieldset). This can be attached to any
|
|
// container element, but will most likely be used with <div>. The role
|
|
// adds semantic meaning to the element for screen readers, and we key
|
|
// the CSS off of the role.
|
|
//
|
|
// `ct-form-box`: Visual styling for encapsulating a block of sub-options.
|
|
// Creates a gray box around elements.
|
|
//
|
|
// <hr>: While this is an element, it has a special meaning and is used
|
|
// to add some vertical spacing to a form.
|
|
//
|
|
//
|
|
// Alternate grid sizing:
|
|
// You can override division of space for controls by adding a class
|
|
// at grid level (.ct-form) to adjust size for "split" widgets:
|
|
// `ct-form-maxmin: First widget is wide; second is small.
|
|
// `ct-form-minmax`: First widget is small; second is wide.
|
|
//
|
|
//
|
|
// Most of the time, you can simply ignore all the optional classes (and
|
|
// attribute and hr element) and simply wrap your labels & controls in
|
|
// a <form class="ct-form"> and layout magic happens.
|
|
|
|
.ct-form {
|
|
// Locally redefine padding to Bootstrap values for this LESS block
|
|
@padding-large-vertical: 1rem;
|
|
@padding-small-vertical: 0.5rem;
|
|
@padding-small-horizontal: 1.5rem;
|
|
// Bootstrap & PatternFly use a 1px border around widgets
|
|
@border-width: 1px;
|
|
@widget-height: 2.25rem; // (36px for PF4 widgets)
|
|
|
|
align-self: start; // Don't vertically fill content by default
|
|
display: grid;
|
|
grid-gap: @padding-small-vertical @padding-small-horizontal;
|
|
// Repeat a label that is a minimum of 4em and its control that
|
|
// fills the remaining space by a CSS variable (default: 2)
|
|
grid-template-columns: repeat(var(--ct-form-columns), max-content 1fr);
|
|
justify-items: stretch;
|
|
align-content: baseline;
|
|
|
|
// All <label> elements describing form elements in PatternFly are
|
|
// supposed to have a `control-label` class.
|
|
// These precede control elements.
|
|
> .control-label {
|
|
text-align: right;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
> :not(hr):not(p) {
|
|
line-height: 2.25rem;
|
|
}
|
|
|
|
> p {
|
|
margin: 0;
|
|
}
|
|
|
|
// Put all control elements to the right of the labels,
|
|
// stretching to the rightmost column
|
|
> :not(.control-label):not(hr):not(.ct-form-split):not(.ct-form-full) {
|
|
grid-column: ~"2 / -1";
|
|
}
|
|
|
|
// Auto-stretch elements to the grid (except when relaxed)
|
|
> :not(.ct-form-relax):not(.spinner) {
|
|
width: auto;
|
|
}
|
|
|
|
// Horizontal rules directly under a form-layout container serve to
|
|
// add some vertical space in forms. This is useful for visually
|
|
// grouping similar elements with whitespace.
|
|
//
|
|
// It's not the same as actually grouping elements (which can be done
|
|
// in the usual ways as well as adding a role="group".
|
|
> hr {
|
|
border: none;
|
|
// LESS needs this to be escaped with ~"". You'll see it below too.
|
|
// CSS wants the string to be 1 / -1 without escaping.
|
|
grid-column: ~"1 / -1";
|
|
height: 0.5rem;
|
|
// Reset padding to ensure all browsers treat this the same
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
// Auto-relax inputs with size
|
|
> input[size],
|
|
> .ct-validation-wrapper > input[size] {
|
|
justify-self: start;
|
|
}
|
|
|
|
> .ct-validation-wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
// Hack to allow number inputs to be sized on WebKit-based browsers
|
|
input[type=number] {
|
|
-webkit-appearance: textarea;
|
|
}
|
|
|
|
// Special considerations for widgets (and widget-like elements)
|
|
// This is a LESS mixin that will not be in the compiled CSS.
|
|
.widget-rules() {
|
|
> input,
|
|
> textarea,
|
|
> select,
|
|
> .bootstrap-select,
|
|
> .ct-select,
|
|
> .dropdown,
|
|
> .combobox-container,
|
|
> fieldset,
|
|
> [role=group],
|
|
> [data-field],
|
|
> .form-group,
|
|
> .btn-group,
|
|
> label.checkbox,
|
|
> label.radio,
|
|
> .checkbox-inline,
|
|
> .radio-inline {
|
|
line-height: var(--pf-global--LineHeight--md);
|
|
}
|
|
}
|
|
|
|
&, > .ct-validation-wrapper {
|
|
.widget-rules();
|
|
}
|
|
|
|
// Some elements need special width considerations
|
|
// as PatternFly normally fixes the width
|
|
> :not(.ct-form-relax):not(.spinner) {
|
|
width: auto !important;
|
|
}
|
|
|
|
// Elements with role="group" are used to group elements —
|
|
// fieldset was going to be used, but Chrome doesn't allow
|
|
// grid or flex placement for fieldsets (yet).
|
|
//
|
|
// Adding a group role is the same thing accessibilty-wise
|
|
// and lets us target all browsers properly.
|
|
//
|
|
// You can use this like:
|
|
// <div role="group">
|
|
//
|
|
// And non-div elements are also supported.
|
|
> [role=group],
|
|
> .ct-validation-wrapper > [role=group] {
|
|
align-self: start;
|
|
align-content: center;
|
|
display: grid;
|
|
grid-gap: @padding-small-vertical;
|
|
min-height: 2.25rem;
|
|
justify-content: start;
|
|
// Only support 3 splits for now (can change to 3 later, if needed)
|
|
grid-template-columns: repeat(3, auto);
|
|
|
|
&.ct-form-vertical {
|
|
> :not(.ct-form-split) {
|
|
// Stretch across the grid (unless it's a split)
|
|
grid-column: ~"1 / -1";
|
|
}
|
|
}
|
|
|
|
> .checkbox,
|
|
> .radio {
|
|
// Spacing is handled by grid, not margin
|
|
margin: 0;
|
|
|
|
&:first-child {
|
|
margin-top: 0.5rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
> [role=group],
|
|
> .ct-validation-wrapper > [role=group],
|
|
> .ct-validation-wrapper > [data-field] {
|
|
// Allow dropdowns to expand as needed
|
|
&:not(.ct-form-relax) {
|
|
> .dropdown {
|
|
width: auto !important;
|
|
}
|
|
|
|
// <select>s need to be coaxed to be 100%
|
|
> .ct-select {
|
|
width: 100%;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vertically align checkboxes and radios properly using flex
|
|
label.checkbox,
|
|
label.radio,
|
|
.checkbox > label,
|
|
.radio > label,
|
|
.checkbox-inline,
|
|
.radio-inline {
|
|
display: inline-flex;
|
|
padding-left: 0;
|
|
padding-right: @padding-small-horizontal;
|
|
align-items: center;
|
|
|
|
> input[type="checkbox"],
|
|
> input[type="radio"] {
|
|
margin: 0 0.5em 0 0;
|
|
position: static;
|
|
}
|
|
}
|
|
|
|
// Remove vertical spacing for fieldsets,
|
|
// as this is handled by the grid gap
|
|
fieldset {
|
|
> .checkbox,
|
|
> .radio {
|
|
&:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// List groups override the grid gap, so we're adding it manually
|
|
.list-group {
|
|
margin-bottom: @padding-small-vertical;
|
|
}
|
|
|
|
// Relax split elements to only take up one column
|
|
> .ct-form-split {
|
|
grid-column: ~"auto / auto";
|
|
}
|
|
|
|
// Stretch to full width
|
|
> .ct-form-full {
|
|
grid-column: ~"1 / -1";
|
|
}
|
|
|
|
// Move warnings, errors, info, etc. up a bit to associate with previous field
|
|
.bump-up() {
|
|
position: relative;
|
|
margin-top: -0.5rem;
|
|
}
|
|
|
|
> .has-success,
|
|
> .has-warning,
|
|
> .has-error {
|
|
&:not(.form-group):not(fieldset):not([role=group]) {
|
|
//.bump-up();
|
|
}
|
|
}
|
|
|
|
> .help-block {
|
|
.bump-up();
|
|
}
|
|
|
|
.help-block {
|
|
--help-line-height: calc(var(--pf-global--LineHeight--md) * 1rem);
|
|
line-height: var(--help-line-height);
|
|
|
|
&:empty {
|
|
display: none;
|
|
}
|
|
|
|
.spinner {
|
|
position: relative;
|
|
// (baseline - height - border) / 2
|
|
top: calc((var(--help-line-height) - 16px - 2px) / 2);
|
|
}
|
|
}
|
|
|
|
.ct-form-box {
|
|
background: var(--color-gray-1);
|
|
border-width: 1px;
|
|
border-style: solid;
|
|
border-color: var(--color-gray-5);
|
|
padding: 0.5rem;
|
|
padding-top: 1rem;
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
// Force a form element to stretch. Add as a class to `form-control`.
|
|
.ct-form-stretch {
|
|
justify-content: stretch !important;
|
|
}
|
|
|
|
// Instruct a `form-control` to not stretch.
|
|
.ct-form-relax {
|
|
justify-self: start;
|
|
}
|
|
|
|
// Reset .ct-form-split for small dialogs, as they don't have
|
|
// much width. This allows for using the same HTML layout in both
|
|
// narrow and normal dialogs.
|
|
.modal-dialog.modal-sm .ct-form > .ct-form-split {
|
|
grid-column: ~"2 / -1";
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
// When inside of lists or modals & the page isn't wide enough,
|
|
// collapse (label + control) columns down to 1, to force splits on
|
|
// their own lines
|
|
.listing-ct-body,
|
|
.modal {
|
|
--ct-form-columns: 1;
|
|
}
|
|
}
|
|
|
|
// Alternate layout, for a split, used at ct-form grid-level:
|
|
// First form widget is as small as possible;
|
|
// Second takes up the rest of the space
|
|
.ct-form-minmax {
|
|
grid-template-columns: max-content min-content max-content 1fr;
|
|
}
|
|
|
|
// Alternate layout, for a split, used at ct-form grid-level:
|
|
// First form widget takes up as much space as it can;
|
|
// Second form widget is as small as possible
|
|
.ct-form-maxmin {
|
|
grid-template-columns: max-content 1fr max-content min-content;
|
|
}
|
|
|
|
@media (max-width: @screen-xs) {
|
|
// When inside of lists or modals & the page is *very* narrow,
|
|
// collapse the grid further, so labels are above controls
|
|
//
|
|
// Note: Padding variables below are outside the local scope of the
|
|
// .ct-form block, so they default to the global PatternFly
|
|
// values.
|
|
|
|
.listing-ct-body,
|
|
.modal {
|
|
.ct-form {
|
|
// Completely deconstruct the grid layout
|
|
grid-template-columns: initial;
|
|
|
|
> * {
|
|
// Don't restrict grid placement
|
|
grid-column: auto;
|
|
max-width: 100%;
|
|
}
|
|
|
|
// As control labels fill the row, left align and remove padding
|
|
> .control-label {
|
|
padding: 0;
|
|
text-align: left;
|
|
|
|
// Everything but the first label should have space to breathe
|
|
&:not(:first-child) {
|
|
margin: @padding-large-vertical 0 0;
|
|
}
|
|
}
|
|
|
|
// Reduce vertical height spacing between groups of elements
|
|
> hr {
|
|
height: @padding-large-vertical * 2;
|
|
}
|
|
}
|
|
}
|
|
}
|