Skip to content

Commit

Permalink
feat: implement controlled popover, add styles for select; move selec…
Browse files Browse the repository at this point in the history
…t from forms to collections
  • Loading branch information
josemarluedke committed Feb 19, 2024
1 parent d1bbcea commit 6ff7c9d
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 34 deletions.
3 changes: 2 additions & 1 deletion packages/collections/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
"app-js": {
"./components/dropdown.js": "./dist/_app_/components/dropdown.js",
"./components/listbox.js": "./dist/_app_/components/listbox.js",
"./components/native-select.js": "./dist/_app_/components/native-select.js"
"./components/native-select.js": "./dist/_app_/components/native-select.js",
"./components/select.js": "./dist/_app_/components/select.js"
}
},
"exports": {
Expand Down
4 changes: 2 additions & 2 deletions packages/collections/src/components/dropdown.gts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface DropdownArgs
| 'shiftOptions'
| 'offsetOptions'
| 'strategy'
| 'onClose'
| 'didClose'
> {
/**
* Whether the dropdown should close upon selecting an item.
Expand Down Expand Up @@ -58,7 +58,7 @@ class Dropdown extends Component<DropdownSignature> {
@shiftOptions={{@shiftOptions}}
@offsetOptions={{@offsetOptions}}
@strategy={{@strategy}}
@onClose={{@onClose}}
@didClose={{@didClose}}
as |p|
>
{{yield
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import Component from '@glimmer/component';
import type { TOC } from '@ember/component/template-only';
import { tracked } from '@glimmer/tracking';
import { on } from '@ember/modifier';

Check failure on line 4 in packages/collections/src/components/select.gts

View workflow job for this annotation

GitHub Actions / Tests

'on' is defined but never used
import {
NativeSelect,
Listbox,
type ListboxSignature,
type ListItemNode
} from '@frontile/collections';
import { NativeSelect } from './native-select';

import { Listbox, type ListboxSignature, type ListItemNode } from './listbox';

import { useStyles } from '@frontile/theme';
import { VisuallyHidden } from '@frontile/utilities';
import {
Expand All @@ -19,7 +18,7 @@ import { hash } from '@ember/helper';
import type { ModifierLike } from '@glint/template';

Check failure on line 18 in packages/collections/src/components/select.gts

View workflow job for this annotation

GitHub Actions / Tests

'ModifierLike' is defined but never used
import type { WithBoundArgs } from '@glint/template';

Check failure on line 19 in packages/collections/src/components/select.gts

View workflow job for this annotation

GitHub Actions / Tests

'WithBoundArgs' is defined but never used

interface FormFieldSelectArgs<T = unknown>
interface SelectArgs<T = unknown>
extends Pick<
PopoverSignature['Args'],
| 'placement'
Expand All @@ -28,7 +27,7 @@ interface FormFieldSelectArgs<T = unknown>
| 'shiftOptions'
| 'offsetOptions'
| 'strategy'
| 'onClose'
| 'didClose'
>,
Pick<
ListboxSignature<T>['Args'],
Expand All @@ -40,6 +39,7 @@ interface FormFieldSelectArgs<T = unknown>
| 'allowEmpty'
| 'onSelectionChange'
| 'items'
| 'onAction'
>,
Pick<
ContentSignature['Args'],
Expand Down Expand Up @@ -73,14 +73,19 @@ interface FormFieldSelectArgs<T = unknown>
disableFocusTrap?: boolean;
}

interface FormFieldSelectSignature {
Args: FormFieldSelectArgs;
interface SelectSignature {
Args: SelectArgs;
Element: HTMLUListElement | HTMLSelectElement;
Blocks: ListboxSignature['Blocks'];
}

class FormFieldSelect extends Component<FormFieldSelectSignature> {
class Select extends Component<SelectSignature> {
@tracked nodes: ListItemNode[] = [];
@tracked isOpen = false;

onOpenChange = (isOpen: boolean) => {
this.isOpen = isOpen;
};

get blockScroll() {
if (this.args.blockScroll === false) {
Expand All @@ -97,7 +102,16 @@ class FormFieldSelect extends Component<FormFieldSelectSignature> {
}

onAction = (key: string) => {
// todo
if (typeof this.args.onAction === 'function') {
this.args.onAction(key);
}

if (
this.args.closeOnItemSelect !== false &&
this.args.selectionMode !== 'multiple'
) {
this.isOpen = false;
}
};

get selectedText() {
Expand Down Expand Up @@ -125,6 +139,15 @@ class FormFieldSelect extends Component<FormFieldSelectSignature> {
return this.args.backdrop;
}

get classNames() {
const { select } = useStyles();
const { base, icon } = select();
return {
base: base({ class: this.args.class }),
icon: icon()
};
}

<template>
<Popover
@placement={{@placement}}
Expand All @@ -133,7 +156,9 @@ class FormFieldSelect extends Component<FormFieldSelectSignature> {
@shiftOptions={{@shiftOptions}}
@offsetOptions={{@offsetOptions}}
@strategy={{@strategy}}
@onClose={{@onClose}}
@didClose={{@didClose}}
@isOpen={{this.isOpen}}
@onOpenChange={{this.onOpenChange}}
as |p|
>
<VisuallyHidden>
Expand All @@ -149,6 +174,7 @@ class FormFieldSelect extends Component<FormFieldSelectSignature> {
>
<:item as |l|>
{{#if (has-block "item")}}
{{! @glint-expect-error: the signature of the native select item is not the same as the listtbox item}}
{{yield l to="item"}}
{{else}}
<l.Item @key={{l.key}}>
Expand All @@ -157,14 +183,23 @@ class FormFieldSelect extends Component<FormFieldSelectSignature> {
{{/if}}
</:item>
<:default as |l|>
{{! @glint-expect-error: the signature of the native select is not the same as the listtbox}}
{{yield l to="default"}}
</:default>
</NativeSelect>
</VisuallyHidden>
<button {{p.trigger}} {{p.anchor}} data-test-id="trigger">
{{this.selectedTextValue}}
BUTTON
<button
{{p.trigger}}
{{p.anchor}}
data-test-id="trigger"
class={{this.classNames.base}}
>
<span>
{{this.selectedTextValue}}
</span>
<Icon class={{this.classNames.icon}} />
</button>
<p.Content
Expand Down Expand Up @@ -217,5 +252,25 @@ class FormFieldSelect extends Component<FormFieldSelectSignature> {
</template>
}
export { FormFieldSelect, type FormFieldSelectSignature };
export default FormFieldSelect;
const Icon: TOC<{
Element: SVGElement;
}> = <template>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
aria-hidden="true"
...attributes
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"
/>
</svg>
</template>;

export { Select, type SelectSignature };
export default Select;
156 changes: 156 additions & 0 deletions packages/collections/src/components/select.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
label: New
---
# Select


## Import

```js
import { Select } from '@frontile/collections';
```

## Usage

```gts preview
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { array } from '@ember/helper';
import { Select } from '@frontile/collections';
import { Divider } from '@frontile/utilities';
const animals = [
'cheetah',
'crocodile',
'elephant',
'giraffe',
'kangaroo',
'koala',
'lemming',
'lemur',
'lion',
'lobster',
'panda',
'penguin',
'tiger',
'zebra'
];
const animalsAsOject = [
{ key: 'cheetah', label: 'Cheetah' },
{ key: 'crocodile', label: 'Crocodile' },
{ key: 'elephant', label: 'Elephant' },
{ key: 'giraffe', label: 'Giraffe' },
{ key: 'kangaroo', label: 'Kangaroo' },
{ key: 'koala', label: 'Koala' },
{ key: 'lemming', label: 'Lemming' },
{ key: 'lemur', label: 'Lemur' },
{ key: 'lion', label: 'Lion' },
{ key: 'lobster', label: 'Lobster' },
{ key: 'panda', label: 'Panda' },
{ key: 'penguin', label: 'Penguin' },
{ key: 'tiger', label: 'Tiger' },
{ key: 'zebra', label: 'Zebra' }
];
export default class Example extends Component {
@tracked selectedKeys: string[] = [];
@tracked selectedKeys2: string[] = ['elephant'];
@tracked selectedKeys3: string[] = [];
@action
onChange(value: boolean): void {
this.isSelected = value;
}
@action
onAction(key: string) {
// eslint-disable-next-line
console.log('Click on key', key);
}
@action
onSelectionChange(keys: string[]) {
this.selectedKeys = keys;
}
@action
onSelectionChange2(keys: string[]) {
this.selectedKeys2 = keys;
}
@action
onSelectionChange3(keys: string[]) {
this.selectedKeys3 = keys;
}
<template>
<Select
@selectionMode="multiple"
@items={{animals}}
@onAction={{this.onAction}}
@selectedKeys={{this.selectedKeys}}
@onSelectionChange={{this.onSelectionChange}}
>
<:item as |o|>
<o.Item @key={{o.item}} @intent="default" @appearance="faded">
{{o.item}}
</o.Item>
</:item>
</Select>
Values:
{{this.selectedKeys}}
<Divider @class="my-8" />
<Select
@placeholder="select an option my friend"
@allowEmpty={{true}}
@selectionMode="single"
@items={{animalsAsOject}}
@intent="primary"
@selectedKeys={{this.selectedKeys2}}
@onAction={{this.onAction}}
@onSelectionChange={{this.onSelectionChange2}}
/>
Values:
{{this.selectedKeys2}}
<Divider @class="my-8" />
<Select
@disableTransitions={{true}}
@onAction={{this.onAction}}
@disabledKeys={{(array "notifications")}}
@selectedKeys={{this.selectedKeys3}}
@onSelectionChange={{this.onSelectionChange3}}
as |l|
>
<l.Item @key="profile" @description="View my profile">
My Provile
</l.Item>
<l.Item @key="settings" @shortcut="⌘⇧S">Settings</l.Item>
<l.Item @key="notifications" @shortcut="⌘⇧N" @withDivider={{true}}>
Notifications
</l.Item>
<l.Item @key="reset" @intent="danger" @class="text-danger">
<:start>
<div>Start</div>
</:start>
<:default>
Reset Settings
</:default>
</l.Item>
<l.Item
@key="delete"
@shortcut="⌘⇧D"
@intent="danger"
@appearance="faded"
@class="text-danger"
>
Delete Account
</l.Item>
</Select>
Values:
{{this.selectedKeys3}}
</template>
}```


1 change: 1 addition & 0 deletions packages/collections/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './components/listbox';
export * from './components/dropdown';
export * from './components/native-select';
export * from './components/select';
1 change: 0 additions & 1 deletion packages/forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"./components/form-field/input.js": "./dist/_app_/components/form-field/input.js",
"./components/form-field/label.js": "./dist/_app_/components/form-field/label.js",
"./components/form-field/radio.js": "./dist/_app_/components/form-field/radio.js",
"./components/form-field/select.js": "./dist/_app_/components/form-field/select.js",
"./components/form-field/textarea.js": "./dist/_app_/components/form-field/textarea.js",
"./components/form-input.js": "./dist/_app_/components/form-input.js",
"./components/form-radio-group.js": "./dist/_app_/components/form-radio-group.js",
Expand Down
Loading

0 comments on commit 6ff7c9d

Please sign in to comment.