Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rolemodel/optics",
"version": "2.1.5",
"version": "2.2.0",
"packageManager": "yarn@4.8.1",
"description": "Optics is a css package that provides base styles and components that can be integrated and customized in a variety of projects.",
"main": "dist/css/optics.css",
Expand Down
3 changes: 2 additions & 1 deletion src/components/form.css
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ textarea.form-control:not([type='radio'], [type='checkbox']) {
.form-label,
.form-error,
.form-hint,
.form-control:not([type='radio'], [type='checkbox']) {
.form-control:not([type='radio'], [type='checkbox']),
.segmented-control {
grid-column: 1 / 3;
}
/* stylelint-enable no-descending-specificity */
Expand Down
1 change: 1 addition & 0 deletions src/components/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@import 'modal';
@import 'navbar';
@import 'pagination';
@import 'segmented-control';
@import 'sidebar';
@import 'side_panel';
@import 'spinner';
Expand Down
135 changes: 135 additions & 0 deletions src/components/segmented-control.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
.segmented-control {
--op-input-inner-focus: 0 0 0 var(--op-border-width-large);
--op-input-focus-primary:
var(--op-input-inner-focus) var(--op-color-primary-plus-two),
var(--op-input-outer-focus) var(--op-color-primary-plus-five);

/* Public API (customizable component options) */
--_op-segmented-control-height-small: var(--op-input-height-small);
--_op-segmented-control-height-medium: var(--op-input-height-medium);
--_op-segmented-control-height-large: var(--op-input-height-large);

--_op-segmented-control-font-small: var(--op-font-x-small);
--_op-segmented-control-font-medium: var(--op-font-small);
--_op-segmented-control-font-large: var(--op-font-small);

--_op-segmented-control-label-padding-small: var(--op-space-x-small);
--_op-segmented-control-label-padding-medium: var(--op-space-small);
--_op-segmented-control-label-padding-large: var(--op-space-small);

--_op-segmented-control-label-gap-small: var(--op-space-3x-small);
--_op-segmented-control-label-gap-medium: var(--op-space-2x-small);
--_op-segmented-control-label-gap-large: var(--op-space-2x-small);

--_op-segmented-control-color-icon-default: var(--op-color-neutral-on-plus-eight-alt);
--_op-segmented-control-color-icon-active: light-dark(
var(--op-color-primary-plus-four),
var(--op-color-primary-minus-six)
);

/* Private API (component option defaults) */
--__op-segmented-control-height: var(--_op-segmented-control-height-large);
--__op-segmented-control-font-size: var(--_op-segmented-control-font-large);
--__op-segmented-control-label-padding: var(--_op-segmented-control-label-padding-large);
--__op-segmented-control-label-gap: var(--_op-segmented-control-label-gap-large);
--__op-segmented-control-color-icon: var(--_op-segmented-control-color-icon-default);

position: relative;
display: grid;
width: fit-content;
height: var(--__op-segmented-control-height);
padding: var(--op-space-2x-small);
border-radius: var(--op-radius-medium);
background-color: var(--op-color-neutral-plus-eight);
box-shadow: var(--op-border-all) var(--op-color-border);
color: var(--op-color-neutral-on-plus-eight);
font-size: var(--__op-segmented-control-font-size);
gap: var(--op-space-2x-small);
grid-auto-flow: column;

&.segmented-control--full-width {
width: 100%;
}

.icon {
color: var(--__op-segmented-control-color-icon);
}

.segmented-control__input {
position: absolute;
overflow: hidden;
width: 1px;
height: 1px;
padding: 0;
border-width: 0;
margin: -1px;
clip: rect(0, 0, 0, 0);
white-space: nowrap;

/* Selected Option */
&:checked + .segmented-control__label {
--__op-segmented-control-color-icon: var(--_op-segmented-control-color-icon-active);

background-color: var(--op-color-primary-plus-one);
color: var(--op-color-primary-on-plus-one);
}

&:focus-visible {
+ .segmented-control__label {
box-shadow: var(--op-input-focus-primary);
}
}
}

.segmented-control__label {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--op-radius-small);
gap: var(--__op-segmented-control-label-gap);
padding-inline: var(--__op-segmented-control-label-padding);
white-space: nowrap;

&:hover {
background-color: var(--op-color-neutral-plus-five);
color: var(--op-color-neutral-on-plus-five);
}
}

/* Size Modifiers */
&.segmented-control--small {
--__op-segmented-control-height: var(--_op-segmented-control-height-small);
--__op-segmented-control-font-size: var(--_op-segmented-control-font-small);
--__op-segmented-control-label-padding: var(--_op-segmented-control-label-padding-small);
--__op-segmented-control-label-gap: var(--_op-segmented-control-label-gap-small);

.icon {
--__op-icon-font-size: var(--_op-icon-font-size-small);
--__op-icon-optical-size: var(--_op-icon-optical-size-small);
}
}

&.segmented-control--medium {
--__op-segmented-control-height: var(--_op-segmented-control-height-medium);
--__op-segmented-control-font-size: var(--_op-segmented-control-font-medium);
--__op-segmented-control-label-padding: var(--_op-segmented-control-label-padding-medium);
--__op-segmented-control-label-gap: var(--_op-segmented-control-label-gap-medium);

.icon {
--__op-icon-font-size: var(--_op-icon-font-size-medium);
--__op-icon-optical-size: var(--_op-icon-optical-size-medium);
}
}

&.segmented-control--large {
--__op-segmented-control-height: var(--_op-segmented-control-height-large);
--__op-segmented-control-font-size: var(--_op-segmented-control-font-large);
--__op-segmented-control-label-padding: var(--_op-segmented-control-label-padding-large);
--__op-segmented-control-label-gap: var(--_op-segmented-control-label-gap-large);

.icon {
--__op-icon-font-size: var(--_op-icon-font-size-medium);
--__op-icon-optical-size: var(--_op-icon-optical-size-medium);
}
}
}
2 changes: 1 addition & 1 deletion src/stories/Components/Avatar/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const createAvatar = ({
}) => {
const element = document.createElement(useLink ? 'a' : 'div')

element.href = '/?path=/docs/content-components-avatar--docs'
element.href = '/?path=/docs/components-avatar--docs'
element.className = `avatar avatar--${size}`

const image = document.createElement('img')
Expand Down
2 changes: 1 addition & 1 deletion src/stories/Components/Badge/Badge.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { createSourceCodeLink } from '../../helpers/sourceCodeLink.js'
}}
></div>

The Badge component is similar to the Tag component, however it has a different semantic purpose. Badge is intended to be used for notification and information where Tag is intended to be used for interaction and input. See [Tag](?path=/docs/content-components-tag--docs) for details on its usage.
The Badge component is similar to the Tag component, however it has a different semantic purpose. Badge is intended to be used for notification and information where Tag is intended to be used for interaction and input. See [Tag](?path=/docs/components-tag--docs) for details on its usage.

## Playground

Expand Down
2 changes: 1 addition & 1 deletion src/stories/Components/Breadcrumbs/Breadcrumbs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Breadcrumbs can be used as a standalone component, however, it does have a few d

### Separator

`.breadcrumbs__separator` is used to separate the links in the breadcrumbs. It is a div that anything can be put within, however typically would be used with an [Icon](?path=/docs/visual-components-icon--docs).
`.breadcrumbs__separator` is used to separate the links in the breadcrumbs. It is a div that anything can be put within, however typically would be used with an [Icon](?path=/docs/components-icon--docs).

<Canvas of={BreadcrumbsStories.SeparatorIcon} />

Expand Down
4 changes: 3 additions & 1 deletion src/stories/Components/ButtonGroup/ButtonGroup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { createSourceCodeLink } from '../../helpers/sourceCodeLink.js'
}}
></div>

`.btn-group` is a class that can be added on a container around a collection of buttons. It provides grouping styles for any button conbination and variation that exists in the [Button Component](?path=/docs/navigation-components-button--docs)
`.btn-group` is a class that can be added on a container around a collection of buttons. It provides grouping styles for any button conbination and variation that exists in the [Button Component](?path=/docs/components-button--docs)

Button Group is similar to the Segmented Control component, however it has a different semantic purpose. Button Group is intended to be used for grouping related actions or navigational buttons where Segmented Control is intended to be used for form option selection and submission. See [Segmented Control](?path=/docs/components-segmented-control--docs) for details on its usage.

## Playground

Expand Down
2 changes: 1 addition & 1 deletion src/stories/Components/ContentHeader/ContentHeader.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ This example demonstrates a content header with minimal customization achieving

The content header classes are structured using the [BEM methodology](https://getbem.com/naming).

This allows us to define core styles on a main [block](https://getbem.com/naming/#block) class, and use [modifiers](https://getbem.com/naming/#modifier) to encapsulate variant styles. You can modify all content header behavior by overriding the `.content-header` placeholder selector and setting any properties:
This allows us to define core styles on a main [block](https://getbem.com/naming/#block) class, and use [modifiers](https://getbem.com/naming/#modifier) to encapsulate variant styles. You can modify all content header behavior by overriding the `.content-header` class and setting any properties:

```css
.content-header {
Expand Down
4 changes: 1 addition & 3 deletions src/stories/Components/Form/Form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,7 @@ The form classes are structured using the [BEM methodology](https://getbem.com/n

This allows multiple classes to share the same behavior.

Form Controls use multiple placeholder selectors to style all types of controls.

`.form-control` is the placeholder selector that is used to style all form controls.
`.form-control` is the class that is used to style all form controls.

`.form-control:not([type='radio'], [type='checkbox'])` is the selector that is used to style form controls based on the input like color, date, text, number, etc.

Expand Down
2 changes: 1 addition & 1 deletion src/stories/Components/Navbar/Navbar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ For instructions on how to integrate a navbar into your applications layout, see

`.navbar` is the main component that everything is contained within.

Any [Button](?path=/docs/navigation-components-button--docs#default) style can be used for the links.
Any [Button](?path=/docs/components-button--docs#default) style can be used for the links.

## Playground

Expand Down
4 changes: 2 additions & 2 deletions src/stories/Components/Pagination/Pagination.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { createAlert } from '../Alert/Alert.js'
Pagination is used to navigate through a series of pages, typically when dealing with tabular data.
A few classes are used in combination with the button component to achieve this.

An example of using this component with the Table component can be found in [Table With Pagination](?path=/docs/content-components-table--docs#with-pagination).
An example of using this component with the Table component can be found in [Table With Pagination](?path=/docs/components-table--docs#with-pagination).

## Note on Implementation

Expand Down Expand Up @@ -51,7 +51,7 @@ Pagination can be used as a standalone component, however, it does have a few de

### Default

`.pagination` Is the main class. It can be placed on a `nav` element and wraps a collection of [buttons](?path=/docs/navigation-components-button--docs) to create a pagination component.
`.pagination` Is the main class. It can be placed on a `nav` element and wraps a collection of [buttons](?path=/docs/components-button--docs) to create a pagination component.

The buttons within can use any of the button classes, but form a default look, use the `.btn .btn--no-border .btn--small` classes.

Expand Down
68 changes: 68 additions & 0 deletions src/stories/Components/SegmentedControl/SegmentedControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { createIcon } from '../Icon/Icon.js'

const createOptionInput = (index) => {
const element = document.createElement('input')
element.className = 'segmented-control__input'
element.type = 'radio'
element.id = `option-${index}`
element.value = `value ${index}`
element.name = 'example-segmented-control'

return element
}

const createOptionLabel = (index, showPrefixIcon, showSuffixIcon) => {
const element = document.createElement('label')
element.className = 'segmented-control__label'
element.setAttribute('for', `option-${index}`)
element.innerHTML += '\n'

if (showPrefixIcon) {
element.innerHTML += ' '
element.appendChild(createIcon({ name: 'bolt' }))
element.innerHTML += '\n'
}

element.innerHTML += ` Option ${index} \n `

if (showSuffixIcon) {
element.innerHTML += ' '
element.appendChild(createIcon({ name: 'info' }))
element.innerHTML += '\n '
}

return element
}

export const createSegmentedControl = ({
size = 'large',
fullWidth = false,
showPrefixIcon = false,
showSuffixIcon = false,
options = 3,
}) => {
const element = document.createElement('div')
element.role = 'radiogroup'

element.className = [
'segmented-control',
size === 'large' ? '' : `segmented-control--${size}`,
fullWidth ? 'segmented-control--full-width' : '',
]
.filter(Boolean)
.join(' ')

for (let i = 1; i <= options; i++) {
const input = createOptionInput(i)
const label = createOptionLabel(i, showPrefixIcon, showSuffixIcon)

element.innerHTML += '\n '
element.appendChild(input)
element.innerHTML += '\n '
element.appendChild(label)
}

element.innerHTML += '\n'

return element
}
Loading