@phila/phila-ui-filter-chip 0.2.0-beta.0
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.
- package/README.md +214 -0
- package/dist/FilterChip.vue.d.ts +140 -0
- package/dist/FilterChip.vue.d.ts.map +1 -0
- package/dist/FilterChipBase.vue.d.ts +57 -0
- package/dist/FilterChipBase.vue.d.ts.map +1 -0
- package/dist/FilterChipDropdownPanel.vue.d.ts +26 -0
- package/dist/FilterChipDropdownPanel.vue.d.ts.map +1 -0
- package/dist/FilterChipGroup.vue.d.ts +17 -0
- package/dist/FilterChipGroup.vue.d.ts.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +416 -0
- package/dist/useDragScroll.d.ts +16 -0
- package/dist/useDragScroll.d.ts.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# FilterChip Component
|
|
2
|
+
|
|
3
|
+
<!-- status-badge-start -->
|
|
4
|
+
|
|
5
|
+
### Component Status
|
|
6
|
+
|
|
7
|
+
| Component | Status |
|
|
8
|
+
| --------------- | ------------------------------------------------------------------ |
|
|
9
|
+
| FilterChip |  |
|
|
10
|
+
| FilterChipGroup |  |
|
|
11
|
+
|
|
12
|
+
<!-- status-badge-end -->
|
|
13
|
+
|
|
14
|
+
`@phila/phila-ui-filter-chip` — pill-shaped filter controls for faceted filtering. A chip is a **toggle** when no `choices` are provided, or a **dropdown** (with deferred Apply/Reset) when `choices` is present.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @phila/phila-ui-filter-chip
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Shared model
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import type { FilterDefinition, FilterValues } from "@phila/phila-ui-filter-chip";
|
|
26
|
+
|
|
27
|
+
// Describes one filter axis.
|
|
28
|
+
interface FilterDefinition {
|
|
29
|
+
key: string; // unique key for FilterValues map
|
|
30
|
+
label: string; // chip label
|
|
31
|
+
choices?: FilterChoice[]; // present → dropdown; absent → toggle
|
|
32
|
+
multiple?: boolean; // dropdown only: checkbox (true) vs radio (false)
|
|
33
|
+
iconDefinition?: IconDefinition;
|
|
34
|
+
excludeFromCount?: boolean; // leave out of the filter button's count (e.g. Sort)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Holds the current value of every filter, keyed by FilterDefinition.key.
|
|
38
|
+
type FilterValues = Record<string, string | string[] | boolean>;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`FilterValues` is the single object that represents your entire filter state. Toggle chips store a `boolean`; single-select dropdowns store a `string`; multi-select dropdowns store a `string[]`.
|
|
42
|
+
|
|
43
|
+
## FilterChip — toggle
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
import { ref } from "vue";
|
|
48
|
+
import { FilterChip } from "@phila/phila-ui-filter-chip";
|
|
49
|
+
|
|
50
|
+
const openNow = ref(false);
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<FilterChip text="Open Now" v-model:selected="openNow" />
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## FilterChip — dropdown (single-select)
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<script setup lang="ts">
|
|
62
|
+
import { ref } from "vue";
|
|
63
|
+
import { FilterChip } from "@phila/phila-ui-filter-chip";
|
|
64
|
+
import type { FilterChoice } from "@phila/phila-ui-filter-chip";
|
|
65
|
+
|
|
66
|
+
const districtChoices: FilterChoice[] = [
|
|
67
|
+
{ text: "Center City", value: "center-city" },
|
|
68
|
+
{ text: "North Philadelphia", value: "north-philly" },
|
|
69
|
+
{ text: "West Philadelphia", value: "west-philly" },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const district = ref("");
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<FilterChip label="District" :choices="districtChoices" v-model="district" />
|
|
77
|
+
</template>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Selections are **deferred**: the panel stays open while the user picks, and `update:modelValue` fires only when the user clicks Apply. Reset clears the value without closing.
|
|
81
|
+
|
|
82
|
+
## FilterChip — dropdown (multi-select)
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<template>
|
|
86
|
+
<FilterChip label="Neighborhoods" :choices="neighborhoodChoices" :multiple="true" v-model="selectedNeighborhoods" />
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## FilterChipGroup — data-driven row
|
|
91
|
+
|
|
92
|
+
`FilterChipGroup` renders a row of chips from a `filters` array. The row never wraps: on hover-capable (mouse) devices, hovering reveals `‹`/`›` buttons at the edges to scroll offscreen chips into view; on touch devices the row drag-scrolls instead (a drag suppresses the click that ends it).
|
|
93
|
+
|
|
94
|
+
With `filterButton`, a leading button (sliders icon + a count of total active selections) scrolls with the chips and emits `open-filters` when clicked — wire that to your app's own filter panel. A filter can opt out of the count with `excludeFromCount` (e.g. a Sort chip, which is ordering rather than filtering).
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import { ref } from "vue";
|
|
99
|
+
import { FilterChipGroup } from "@phila/phila-ui-filter-chip";
|
|
100
|
+
import type { FilterDefinition, FilterValues } from "@phila/phila-ui-filter-chip";
|
|
101
|
+
|
|
102
|
+
const filters: FilterDefinition[] = [
|
|
103
|
+
{ key: "openNow", label: "Open Now" }, // toggle
|
|
104
|
+
{ key: "accessible", label: "Accessible" }, // toggle
|
|
105
|
+
{
|
|
106
|
+
key: "district",
|
|
107
|
+
label: "District",
|
|
108
|
+
choices: [
|
|
109
|
+
{ text: "Center City", value: "center-city" },
|
|
110
|
+
{ text: "North Philadelphia", value: "north-philly" },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
key: "amenities",
|
|
115
|
+
label: "Amenities",
|
|
116
|
+
multiple: true,
|
|
117
|
+
choices: [
|
|
118
|
+
{ text: "Parking", value: "parking" },
|
|
119
|
+
{ text: "Wi-Fi", value: "wifi" },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const values = ref<FilterValues>({});
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<FilterChipGroup :filters="filters" v-model="values" />
|
|
129
|
+
</template>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`values` updates whenever any chip is applied or toggled:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// Example shape after interaction:
|
|
136
|
+
{
|
|
137
|
+
openNow: true,
|
|
138
|
+
district: "center-city",
|
|
139
|
+
amenities: ["parking", "wifi"],
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## FilterChipGroup props
|
|
144
|
+
|
|
145
|
+
| Prop | Type | Default | Description |
|
|
146
|
+
| -------------- | -------------------- | ---------- | -------------------------------------------------------------- |
|
|
147
|
+
| `filters` | `FilterDefinition[]` | — | Required. Ordered list of filter axes. |
|
|
148
|
+
| `modelValue` | `FilterValues` | `{}` | Current filter state (`v-model`). |
|
|
149
|
+
| `label` | `string` | — | Optional row label rendered above chips. |
|
|
150
|
+
| `size` | `ComponentSize` | `"medium"` | Chip size applied to all chips. |
|
|
151
|
+
| `color` | `FilterChipColor` | `"blue"` | Chip color applied to all chips. |
|
|
152
|
+
| `filterButton` | `boolean` | `false` | Show a leading sliders button with the active-selection count. |
|
|
153
|
+
|
|
154
|
+
## FilterChipGroup events
|
|
155
|
+
|
|
156
|
+
| Event | Payload | Description |
|
|
157
|
+
| ------------------- | -------------- | ------------------------------------------------------------------ |
|
|
158
|
+
| `update:modelValue` | `FilterValues` | Next filter state when any chip is applied or toggled (`v-model`). |
|
|
159
|
+
| `open-filters` | — | The `filterButton` was clicked; open your app's filter panel. |
|
|
160
|
+
|
|
161
|
+
## FilterChip props
|
|
162
|
+
|
|
163
|
+
| Prop | Type | Default | Description |
|
|
164
|
+
| ------------------------ | -------------------- | ---------- | -------------------------------------------------------------- |
|
|
165
|
+
| `label` | `string` | — | Chip label (also the group label in dropdown mode). |
|
|
166
|
+
| `text` | `string` | — | Alias of `label` for toggle mode. |
|
|
167
|
+
| `size` | `ComponentSize` | `"medium"` | Chip size. |
|
|
168
|
+
| `color` | `FilterChipColor` | `"blue"` | Chip color. |
|
|
169
|
+
| `iconDefinition` | `IconDefinition` | — | Optional leading FA icon shown inside the chip. |
|
|
170
|
+
| `trailingIconDefinition` | `IconDefinition` | — | Toggle mode: optional trailing icon (dropdown uses a chevron). |
|
|
171
|
+
| `selected` | `boolean` | `false` | Toggle mode: active state (`v-model:selected`). |
|
|
172
|
+
| `choices` | `FilterChoice[]` | — | Presence switches to dropdown mode. |
|
|
173
|
+
| `multiple` | `boolean` | `false` | Dropdown mode: multi-select (checkbox) vs single (radio). |
|
|
174
|
+
| `modelValue` | `string \| string[]` | — | Dropdown mode: bound value (`v-model`). |
|
|
175
|
+
| `applyText` | `string` | `"Apply"` | Label for the Apply button. |
|
|
176
|
+
| `resetText` | `string` | `"Reset"` | Label for the Reset button. |
|
|
177
|
+
|
|
178
|
+
A toggle chip with an `iconDefinition` but no label/text renders the icon **centered** as the chip's content (icon-only), rather than as a leading icon beside an empty label. Chip icons are a fixed size regardless of chip size; only the text scales.
|
|
179
|
+
|
|
180
|
+
## FilterChip events
|
|
181
|
+
|
|
182
|
+
| Event | Payload | Description |
|
|
183
|
+
| ------------------- | -------------------- | ------------------------------------------- |
|
|
184
|
+
| `update:selected` | `boolean` | Toggle mode: fired on every click. |
|
|
185
|
+
| `update:modelValue` | `string \| string[]` | Dropdown mode: fired when Apply is clicked. |
|
|
186
|
+
| `click` | `MouseEvent` | Toggle mode: the raw click event. |
|
|
187
|
+
|
|
188
|
+
## Building a custom filter panel
|
|
189
|
+
|
|
190
|
+
`FilterDefinition` and `FilterValues` are the same model that `FilterChipGroup` uses internally. An app can bind its own summary panel or drawer to the same `values` ref — the chip row and the panel are two views over one piece of state. No generic aggregating panel ships in this package.
|
|
191
|
+
|
|
192
|
+
## Development
|
|
193
|
+
|
|
194
|
+
### Install Dependencies
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
pnpm install
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Build Library
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
pnpm build
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Type Check
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pnpm type-check
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { FilterChipProps } from './index';
|
|
2
|
+
declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<FilterChipProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
3
|
+
click: (event: MouseEvent) => any;
|
|
4
|
+
"update:modelValue": (value: string | string[]) => any;
|
|
5
|
+
"update:selected": (value: boolean) => any;
|
|
6
|
+
}, string, import('vue').PublicProps, Readonly<FilterChipProps> & Readonly<{
|
|
7
|
+
onClick?: ((event: MouseEvent) => any) | undefined;
|
|
8
|
+
"onUpdate:modelValue"?: ((value: string | string[]) => any) | undefined;
|
|
9
|
+
"onUpdate:selected"?: ((value: boolean) => any) | undefined;
|
|
10
|
+
}>, {
|
|
11
|
+
text: string;
|
|
12
|
+
size: import('@phila/phila-ui-core').ComponentSize;
|
|
13
|
+
color: import('./index').FilterChipColor;
|
|
14
|
+
iconDefinition: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
15
|
+
trailingIconDefinition: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
16
|
+
label: string;
|
|
17
|
+
choices: import('./index').FilterChoice[];
|
|
18
|
+
multiple: boolean;
|
|
19
|
+
applyText: string;
|
|
20
|
+
resetText: string;
|
|
21
|
+
modelValue: string | string[] | boolean;
|
|
22
|
+
selected: boolean;
|
|
23
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
24
|
+
triggerRef: ({
|
|
25
|
+
$: import('vue').ComponentInternalInstance;
|
|
26
|
+
$data: {};
|
|
27
|
+
$props: {
|
|
28
|
+
readonly text?: string | undefined;
|
|
29
|
+
readonly size?: import('@phila/phila-ui-core').ComponentSize | undefined;
|
|
30
|
+
readonly color?: import('./index').FilterChipColor | undefined;
|
|
31
|
+
readonly iconDefinition?: import('@fortawesome/fontawesome-svg-core').IconDefinition | undefined;
|
|
32
|
+
readonly trailingIconDefinition?: import('@fortawesome/fontawesome-svg-core').IconDefinition | undefined;
|
|
33
|
+
readonly iconOnly?: boolean | undefined;
|
|
34
|
+
readonly active?: boolean | undefined;
|
|
35
|
+
readonly chevron?: boolean | undefined;
|
|
36
|
+
readonly ariaPressed?: boolean | undefined;
|
|
37
|
+
readonly ariaExpanded?: boolean | undefined;
|
|
38
|
+
readonly onClick?: ((event: MouseEvent) => any) | undefined;
|
|
39
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps;
|
|
40
|
+
$attrs: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
};
|
|
43
|
+
$refs: {
|
|
44
|
+
[x: string]: unknown;
|
|
45
|
+
} & {
|
|
46
|
+
rootEl: HTMLButtonElement;
|
|
47
|
+
};
|
|
48
|
+
$slots: Readonly<{
|
|
49
|
+
[name: string]: import('vue').Slot<any> | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
$root: import('vue').ComponentPublicInstance | null;
|
|
52
|
+
$parent: import('vue').ComponentPublicInstance | null;
|
|
53
|
+
$host: Element | null;
|
|
54
|
+
$emit: (event: "click", event: MouseEvent) => void;
|
|
55
|
+
$el: HTMLButtonElement;
|
|
56
|
+
$options: import('vue').ComponentOptionsBase<Readonly<{
|
|
57
|
+
text?: string;
|
|
58
|
+
size?: import('@phila/phila-ui-core').ComponentSize;
|
|
59
|
+
color?: import('./index').FilterChipColor;
|
|
60
|
+
iconDefinition?: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
61
|
+
trailingIconDefinition?: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
62
|
+
iconOnly?: boolean;
|
|
63
|
+
active?: boolean;
|
|
64
|
+
chevron?: boolean;
|
|
65
|
+
ariaPressed?: boolean;
|
|
66
|
+
ariaExpanded?: boolean;
|
|
67
|
+
}> & Readonly<{
|
|
68
|
+
onClick?: ((event: MouseEvent) => any) | undefined;
|
|
69
|
+
}>, {
|
|
70
|
+
el: import('vue').Ref<HTMLButtonElement | null, HTMLButtonElement | null>;
|
|
71
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
72
|
+
click: (event: MouseEvent) => any;
|
|
73
|
+
}, string, {
|
|
74
|
+
text: string;
|
|
75
|
+
size: import('@phila/phila-ui-core').ComponentSize;
|
|
76
|
+
color: import('./index').FilterChipColor;
|
|
77
|
+
iconDefinition: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
78
|
+
trailingIconDefinition: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
79
|
+
iconOnly: boolean;
|
|
80
|
+
active: boolean;
|
|
81
|
+
chevron: boolean;
|
|
82
|
+
}, {}, string, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, import('vue').ComponentProvideOptions> & {
|
|
83
|
+
beforeCreate?: (() => void) | (() => void)[];
|
|
84
|
+
created?: (() => void) | (() => void)[];
|
|
85
|
+
beforeMount?: (() => void) | (() => void)[];
|
|
86
|
+
mounted?: (() => void) | (() => void)[];
|
|
87
|
+
beforeUpdate?: (() => void) | (() => void)[];
|
|
88
|
+
updated?: (() => void) | (() => void)[];
|
|
89
|
+
activated?: (() => void) | (() => void)[];
|
|
90
|
+
deactivated?: (() => void) | (() => void)[];
|
|
91
|
+
beforeDestroy?: (() => void) | (() => void)[];
|
|
92
|
+
beforeUnmount?: (() => void) | (() => void)[];
|
|
93
|
+
destroyed?: (() => void) | (() => void)[];
|
|
94
|
+
unmounted?: (() => void) | (() => void)[];
|
|
95
|
+
renderTracked?: ((e: import('vue').DebuggerEvent) => void) | ((e: import('vue').DebuggerEvent) => void)[];
|
|
96
|
+
renderTriggered?: ((e: import('vue').DebuggerEvent) => void) | ((e: import('vue').DebuggerEvent) => void)[];
|
|
97
|
+
errorCaptured?: ((err: unknown, instance: import('vue').ComponentPublicInstance | null, info: string) => boolean | void) | ((err: unknown, instance: import('vue').ComponentPublicInstance | null, info: string) => boolean | void)[];
|
|
98
|
+
};
|
|
99
|
+
$forceUpdate: () => void;
|
|
100
|
+
$nextTick: typeof import('vue').nextTick;
|
|
101
|
+
$watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infer R ? (...args: [R, R, import('@vue/reactivity').OnCleanup]) => any : (...args: [any, any, import('@vue/reactivity').OnCleanup]) => any, options?: import('vue').WatchOptions): import('vue').WatchStopHandle;
|
|
102
|
+
} & Readonly<{
|
|
103
|
+
text: string;
|
|
104
|
+
size: import('@phila/phila-ui-core').ComponentSize;
|
|
105
|
+
color: import('./index').FilterChipColor;
|
|
106
|
+
iconDefinition: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
107
|
+
trailingIconDefinition: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
108
|
+
iconOnly: boolean;
|
|
109
|
+
active: boolean;
|
|
110
|
+
chevron: boolean;
|
|
111
|
+
}> & Omit<Readonly<{
|
|
112
|
+
text?: string;
|
|
113
|
+
size?: import('@phila/phila-ui-core').ComponentSize;
|
|
114
|
+
color?: import('./index').FilterChipColor;
|
|
115
|
+
iconDefinition?: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
116
|
+
trailingIconDefinition?: import('@fortawesome/fontawesome-svg-core').IconDefinition;
|
|
117
|
+
iconOnly?: boolean;
|
|
118
|
+
active?: boolean;
|
|
119
|
+
chevron?: boolean;
|
|
120
|
+
ariaPressed?: boolean;
|
|
121
|
+
ariaExpanded?: boolean;
|
|
122
|
+
}> & Readonly<{
|
|
123
|
+
onClick?: ((event: MouseEvent) => any) | undefined;
|
|
124
|
+
}>, "el" | ("text" | "size" | "color" | "iconDefinition" | "trailingIconDefinition" | "iconOnly" | "active" | "chevron")> & import('vue').ShallowUnwrapRef<{
|
|
125
|
+
el: import('vue').Ref<HTMLButtonElement | null, HTMLButtonElement | null>;
|
|
126
|
+
}> & {} & import('vue').ComponentCustomProperties & {} & {
|
|
127
|
+
$slots: {
|
|
128
|
+
default?(_: {}): any;
|
|
129
|
+
};
|
|
130
|
+
}) | null;
|
|
131
|
+
}, any>, {
|
|
132
|
+
default?(_: {}): any;
|
|
133
|
+
}>;
|
|
134
|
+
export default _default;
|
|
135
|
+
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
136
|
+
new (): {
|
|
137
|
+
$slots: S;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=FilterChip.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterChip.vue.d.ts","sourceRoot":"","sources":["../src/FilterChip.vue"],"names":[],"mappings":"AA2KA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAsU2qT,GAAG,8CAA8C,GAAG,yBAAyB,GAAG,6DAAmC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA1ElzT,GAAG;;AApPjC,wBA0SK;AAaL,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ComponentSize } from '@phila/phila-ui-core';
|
|
2
|
+
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
3
|
+
import { FilterChipColor } from './index';
|
|
4
|
+
type __VLS_Props = {
|
|
5
|
+
text?: string;
|
|
6
|
+
size?: ComponentSize;
|
|
7
|
+
color?: FilterChipColor;
|
|
8
|
+
iconDefinition?: IconDefinition;
|
|
9
|
+
/** trailing icon (toggle mode); ignored when `chevron` is set. */
|
|
10
|
+
trailingIconDefinition?: IconDefinition;
|
|
11
|
+
/** render only the icon, centered, with no text content. */
|
|
12
|
+
iconOnly?: boolean;
|
|
13
|
+
/** active = selected (toggle) or has-value/open (dropdown). */
|
|
14
|
+
active?: boolean;
|
|
15
|
+
/** show a trailing chevron (dropdown mode). */
|
|
16
|
+
chevron?: boolean;
|
|
17
|
+
/** aria-pressed for toggle; aria-expanded for dropdown. Set by caller. */
|
|
18
|
+
ariaPressed?: boolean;
|
|
19
|
+
ariaExpanded?: boolean;
|
|
20
|
+
};
|
|
21
|
+
declare function __VLS_template(): {
|
|
22
|
+
attrs: Partial<{}>;
|
|
23
|
+
slots: {
|
|
24
|
+
default?(_: {}): any;
|
|
25
|
+
};
|
|
26
|
+
refs: {
|
|
27
|
+
rootEl: HTMLButtonElement;
|
|
28
|
+
};
|
|
29
|
+
rootEl: HTMLButtonElement;
|
|
30
|
+
};
|
|
31
|
+
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
32
|
+
declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {
|
|
33
|
+
el: import('vue').Ref<HTMLButtonElement | null, HTMLButtonElement | null>;
|
|
34
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
35
|
+
click: (event: MouseEvent) => any;
|
|
36
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
37
|
+
onClick?: ((event: MouseEvent) => any) | undefined;
|
|
38
|
+
}>, {
|
|
39
|
+
text: string;
|
|
40
|
+
size: ComponentSize;
|
|
41
|
+
color: FilterChipColor;
|
|
42
|
+
iconDefinition: IconDefinition;
|
|
43
|
+
trailingIconDefinition: IconDefinition;
|
|
44
|
+
iconOnly: boolean;
|
|
45
|
+
active: boolean;
|
|
46
|
+
chevron: boolean;
|
|
47
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
48
|
+
rootEl: HTMLButtonElement;
|
|
49
|
+
}, HTMLButtonElement>;
|
|
50
|
+
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
51
|
+
export default _default;
|
|
52
|
+
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
53
|
+
new (): {
|
|
54
|
+
$slots: S;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=FilterChipBase.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterChipBase.vue.d.ts","sourceRoot":"","sources":["../src/FilterChipBase.vue"],"names":[],"mappings":"AA+PA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,KAAK,WAAW,GAAG;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,cAAc,CAAC;IACxC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AA6DJ,iBAAS,cAAc;WA0GT,OAAO,IAA6B;;yBAXrB,GAAG;;;;;;EAgB/B;AAgBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;UA5MV,MAAM;UACN,aAAa;WACZ,eAAe;oBACN,cAAc;4BAEN,cAAc;cAE5B,OAAO;YAET,OAAO;aAEN,OAAO;;;qBA4MnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FilterChoice } from './index';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
menuId: string;
|
|
4
|
+
label: string;
|
|
5
|
+
choices: FilterChoice[];
|
|
6
|
+
multiple: boolean;
|
|
7
|
+
/** committed value from the parent */
|
|
8
|
+
value: string | string[];
|
|
9
|
+
/** fixed-position coords computed from the trigger rect */
|
|
10
|
+
top: number;
|
|
11
|
+
left: number;
|
|
12
|
+
applyText?: string;
|
|
13
|
+
resetText?: string;
|
|
14
|
+
};
|
|
15
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
16
|
+
reset: () => any;
|
|
17
|
+
apply: (value: string | string[]) => any;
|
|
18
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
19
|
+
onReset?: (() => any) | undefined;
|
|
20
|
+
onApply?: ((value: string | string[]) => any) | undefined;
|
|
21
|
+
}>, {
|
|
22
|
+
applyText: string;
|
|
23
|
+
resetText: string;
|
|
24
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
25
|
+
export default _default;
|
|
26
|
+
//# sourceMappingURL=FilterChipDropdownPanel.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterChipDropdownPanel.vue.d.ts","sourceRoot":"","sources":["../src/FilterChipDropdownPanel.vue"],"names":[],"mappings":"AAmIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,KAAK,WAAW,GAAG;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;;;;;;;;eAFY,MAAM;eACN,MAAM;;AA0LtB,wBASG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FilterChipGroupProps, FilterValues } from './index';
|
|
2
|
+
declare const _default: import('vue').DefineComponent<FilterChipGroupProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
3
|
+
"update:modelValue": (value: FilterValues) => any;
|
|
4
|
+
"open-filters": () => any;
|
|
5
|
+
}, string, import('vue').PublicProps, Readonly<FilterChipGroupProps> & Readonly<{
|
|
6
|
+
"onUpdate:modelValue"?: ((value: FilterValues) => any) | undefined;
|
|
7
|
+
"onOpen-filters"?: (() => any) | undefined;
|
|
8
|
+
}>, {
|
|
9
|
+
size: import('@phila/phila-ui-core').ComponentSize;
|
|
10
|
+
color: import('./index').FilterChipColor;
|
|
11
|
+
modelValue: FilterValues;
|
|
12
|
+
filterButton: boolean;
|
|
13
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
14
|
+
el: HTMLDivElement;
|
|
15
|
+
}, HTMLDivElement>;
|
|
16
|
+
export default _default;
|
|
17
|
+
//# sourceMappingURL=FilterChipGroup.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterChipGroup.vue.d.ts","sourceRoot":"","sources":["../src/FilterChipGroup.vue"],"names":[],"mappings":"AAsOA,OAAO,KAAK,EAAE,oBAAoB,EAAoB,YAAY,EAAE,MAAM,SAAS,CAAC;;;;;;;;;;;;;;;AA8TpF,wBAUG"}
|
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.phila-filter-chip[data-v-7411a108]{display:inline-flex;align-items:center;height:var(--scale-350, 1.75rem);cursor:pointer;border-style:solid;border-radius:var(--border-radius-l);white-space:nowrap;box-sizing:border-box;appearance:none;text-decoration:none;padding:var(--scale-25) var(--scale-75);gap:var(--spacing-xs);border-width:var(--border-width-s)}.phila-filter-chip.is-extra-large[data-v-7411a108]{height:var(--scale-400, 2rem);padding:var(--scale-75) var(--spacing-xs);gap:var(--spacing-2xs)}.phila-filter-chip.is-large[data-v-7411a108]{height:var(--scale-400, 2rem);padding:var(--scale-75) var(--spacing-xs)}.phila-filter-chip.is-small[data-v-7411a108]{height:var(--scale-250, 1.25rem);padding:var(--scale-10) var(--spacing-2xs);gap:var(--spacing-2xs)}.phila-filter-chip.is-icon-only[data-v-7411a108]{padding-left:var(--spacing-s, .75rem);padding-right:var(--spacing-s, .75rem)}.phila-filter-chip.phila-filter-chip--blue[data-v-7411a108]{color:var(--Schemes-Inverse-Primary);border-color:var(--Schemes-Inverse-Primary);background:var(--Schemes-Info-Container)}.phila-filter-chip.phila-filter-chip--blue.is-active[data-v-7411a108]{background:var(--Palettes-Secondary-Secondary-150);color:var(--Schemes-On-Primary);border-color:var(--Palettes-Tertiary-Tertiary-100)}.phila-filter-chip.phila-filter-chip--blue[data-v-7411a108]:hover:not(:disabled){background:var(--Palettes-Secondary-Secondary-600);color:var(--Palettes-Tertiary-Tertiary-50);border-color:var(--Palettes-Tertiary-Tertiary-100)}.phila-filter-chip.phila-filter-chip--grey[data-v-7411a108]{color:var(--Palettes-Neutral-Variant-Neutral-Variant-200);border-color:var(--Schemes-Border-low);background:var(--Palettes-Neutral-Neutral-700)}.phila-filter-chip.phila-filter-chip--grey.is-active[data-v-7411a108]{background:var(--Palettes-Neutral-Neutral-100);color:var(--Schemes-On-Primary);border-color:var(--Schemes-Border-low)}.phila-filter-chip.phila-filter-chip--grey[data-v-7411a108]:hover:not(:disabled){background:var(--Palettes-Neutral-Neutral-600);color:var(--Palettes-Neutral-Variant-Neutral-Variant-50);border-color:var(--Schemes-Border-low)}.phila-filter-chip.phila-filter-chip--green[data-v-7411a108]{color:var(--Palettes-Success-Success-100);border-color:var(--Palettes-Success-Success-150);background:var(--Palettes-Success-Success-700)}.phila-filter-chip.phila-filter-chip--green.is-active[data-v-7411a108]{background:var(--Palettes-Success-Success-200);color:var(--Schemes-On-Primary);border-color:var(--Palettes-Success-Success-200)}.phila-filter-chip.phila-filter-chip--green[data-v-7411a108]:hover:not(:disabled){background:var(--Palettes-Success-Success-600);color:var(--Palettes-Success-Success-100);border-color:var(--Palettes-Success-Success-150)}.phila-filter-chip.phila-filter-chip--red[data-v-7411a108]{color:var(--Schemes-On-Error-Container);border-color:var(--Schemes-On-Error-Container);background:var(--Schemes-Error-Container)}.phila-filter-chip.phila-filter-chip--red.is-active[data-v-7411a108]{background:var(--Palettes-Error-Error-250);color:var(--Schemes-On-Primary);border-color:var(--Palettes-Error-Error-150)}.phila-filter-chip.phila-filter-chip--red[data-v-7411a108]:hover:not(:disabled){background:var(--Palettes-Error-Error-650);color:var(--Palettes-Error-Error-200);border-color:var(--Palettes-Error-Error-150)}.phila-filter-chip.phila-filter-chip--white[data-v-7411a108]{color:var(--Palettes-Neutral-Variant-Neutral-Variant-200);border-color:var(--Schemes-Border-low);background:var(--Schemes-On-Primary)}.phila-filter-chip.phila-filter-chip--white.is-active[data-v-7411a108]{background:var(--Palettes-Neutral-Neutral-100);color:var(--Schemes-On-Primary);border-color:var(--Schemes-Border-low)}.phila-filter-chip.phila-filter-chip--white[data-v-7411a108]:hover:not(:disabled){background:var(--Schemes-Surface-Container-Lowest);color:var(--Schemes-Inverse-Surface);border-color:var(--Schemes-Border-low)}.phila-filter-chip.phila-filter-chip--yellow[data-v-7411a108]{color:var(--Schemes-On-Alert-Container);border-color:var(--Palettes-Alert-Alert-150);background:var(--Palettes-Alert-Alert-700)}.phila-filter-chip.phila-filter-chip--yellow.is-active[data-v-7411a108]{background:var(--Palettes-Alert-Alert-250);color:var(--Schemes-On-Primary);border-color:var(--Palettes-Alert-Alert-150)}.phila-filter-chip.phila-filter-chip--yellow[data-v-7411a108]:hover:not(:disabled){background:var(--Palettes-Alert-Alert-550);color:var(--Schemes-On-Alert-Container);border-color:var(--Palettes-Alert-Alert-150)}.phila-filter-chip[data-v-7411a108]:focus-visible{outline:2px solid currentColor;outline-offset:var(--spacing-2xs)}.phila-filter-chip-panel{position:fixed;z-index:1000;min-width:12rem;background:var(--Schemes-Background);border:var(--border-width-s) solid var(--Schemes-Border);border-radius:var(--border-radius-s);box-shadow:0 4px 8px #0000001a;padding:var(--spacing-s, .75rem);display:flex;flex-direction:column;gap:var(--spacing-s, .75rem)}.phila-filter-chip-panel__footer{display:flex;justify-content:flex-end;align-items:center;gap:var(--spacing-s, .75rem)}.phila-filter-chip-panel .phila-filter-chip-panel__reset{background:none;border:none;padding:0;cursor:pointer;color:var(--Schemes-Primary);text-decoration:underline;font-weight:600}.phila-filter-chip-panel .phila-filter-chip-panel__reset:focus-visible{outline:2px solid var(--Schemes-Primary);outline-offset:2px}.phila-filter-chip-group[data-v-99d3ffdf]{display:flex;flex-direction:column;gap:var(--spacing-2xs)}.phila-filter-chip-group__viewport[data-v-99d3ffdf]{position:relative}.phila-filter-chip-group__row[data-v-99d3ffdf]{display:flex;flex-wrap:nowrap;gap:var(--spacing-xs);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.phila-filter-chip-group__row[data-v-99d3ffdf]::-webkit-scrollbar{display:none}.phila-filter-chip-group__arrow[data-v-99d3ffdf]{position:absolute;top:50%;transform:translateY(-50%);z-index:1;display:none;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border-radius:50%;border:var(--border-width-s) solid var(--Schemes-Border);background:var(--Schemes-Background);color:var(--Schemes-On-Surface);box-shadow:0 6px 8px -2px #00000059;cursor:pointer}.phila-filter-chip-group__arrow--left[data-v-99d3ffdf]{left:0}.phila-filter-chip-group__arrow--right[data-v-99d3ffdf]{right:0}@media(hover:hover)and (pointer:fine){.phila-filter-chip-group__viewport:hover .phila-filter-chip-group__arrow[data-v-99d3ffdf]{display:flex}}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BaseProps, ComponentSize } from '@phila/phila-ui-core';
|
|
2
|
+
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
|
3
|
+
export { default as FilterChip } from './FilterChip.vue';
|
|
4
|
+
export { default as FilterChipGroup } from './FilterChipGroup.vue';
|
|
5
|
+
export type FilterChipColor = "blue" | "grey" | "green" | "red" | "white" | "yellow";
|
|
6
|
+
/** value is string to satisfy RadioGroup (string-only) and CheckboxGroup. */
|
|
7
|
+
export interface FilterChoice {
|
|
8
|
+
text: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FilterDefinition {
|
|
12
|
+
key: string;
|
|
13
|
+
label: string;
|
|
14
|
+
/** Present → dropdown chip; absent → toggle chip. */
|
|
15
|
+
choices?: FilterChoice[];
|
|
16
|
+
/** Dropdown only: checkbox group (true) vs radio group (false). Default false. */
|
|
17
|
+
multiple?: boolean;
|
|
18
|
+
iconDefinition?: IconDefinition;
|
|
19
|
+
/** Leave this filter's selections out of the filter button's count (e.g. Sort). */
|
|
20
|
+
excludeFromCount?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type FilterValues = Record<string, string | string[] | boolean>;
|
|
23
|
+
/** A single chip. Toggle mode when `choices` is absent; dropdown mode when present. */
|
|
24
|
+
export interface FilterChipProps extends BaseProps {
|
|
25
|
+
/** Chip label (also the radio/checkbox group label in dropdown mode). */
|
|
26
|
+
label?: string;
|
|
27
|
+
/** Toggle label when no choices (alias of label via slot). */
|
|
28
|
+
text?: string;
|
|
29
|
+
size?: ComponentSize;
|
|
30
|
+
color?: FilterChipColor;
|
|
31
|
+
iconDefinition?: IconDefinition;
|
|
32
|
+
/** Toggle mode: trailing icon (ignored in dropdown mode, which uses a chevron). */
|
|
33
|
+
trailingIconDefinition?: IconDefinition;
|
|
34
|
+
/** Toggle mode: selection state. */
|
|
35
|
+
selected?: boolean;
|
|
36
|
+
/** Dropdown mode: presence switches the chip to a dropdown. */
|
|
37
|
+
choices?: FilterChoice[];
|
|
38
|
+
/** Dropdown mode: multi (checkbox) vs single (radio). */
|
|
39
|
+
multiple?: boolean;
|
|
40
|
+
/** Dropdown mode: bound value (string for single, string[] for multi). */
|
|
41
|
+
modelValue?: string | string[] | boolean;
|
|
42
|
+
applyText?: string;
|
|
43
|
+
resetText?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface FilterChipGroupProps extends BaseProps {
|
|
46
|
+
filters: FilterDefinition[];
|
|
47
|
+
modelValue?: FilterValues;
|
|
48
|
+
label?: string;
|
|
49
|
+
size?: ComponentSize;
|
|
50
|
+
color?: FilterChipColor;
|
|
51
|
+
/** Show a leading button (sliders icon + active-selection count) that emits `open-filters`. */
|
|
52
|
+
filterButton?: boolean;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEnE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErF,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC;AAEvE,uFAAuF;AACvF,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,mFAAmF;IACnF,sBAAsB,CAAC,EAAE,cAAc,CAAC;IACxC,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,+FAA+F;IAC/F,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require('./index.css');const e=require("vue"),h=require("@phila/phila-ui-core"),z=require("@fortawesome/pro-solid-svg-icons"),T=require("@phila/phila-ui-radio"),E=require("@phila/phila-ui-checkbox"),P=require("@phila/phila-ui-button"),F=["aria-pressed","aria-expanded"],w="xxsmall",R=e.defineComponent({__name:"FilterChipBase",props:{text:{default:void 0},size:{default:"medium"},color:{default:"blue"},iconDefinition:{default:void 0},trailingIconDefinition:{default:void 0},iconOnly:{type:Boolean,default:!1},active:{type:Boolean,default:!1},chevron:{type:Boolean,default:!1},ariaPressed:{type:Boolean},ariaExpanded:{type:Boolean}},emits:["click"],setup(t,{expose:m}){const l=t,r=e.ref(null),i=e.computed(()=>h.cn("phila-filter-chip",`phila-filter-chip--${l.color}`,l.size!=="medium"&&`is-${l.size}`,"has-text-body-"+u(l.size),l.active&&"is-active",l.iconOnly&&"is-icon-only"));m({el:r});const u=a=>{switch(a){case"extra-large":return"default";case"large":return"small";default:return"extra-small"}};return(a,y)=>(e.openBlock(),e.createElementBlock("button",{ref_key:"rootEl",ref:r,type:"button",class:e.normalizeClass(i.value),"aria-pressed":t.ariaPressed,"aria-expanded":t.ariaExpanded,onClick:y[0]||(y[0]=v=>a.$emit("click",v))},[e.createVNode(e.unref(h.ActionContent),{"icon-definition":t.iconDefinition,size:t.size,inline:"","icon-size":w,"icon-only":t.iconOnly},{default:e.withCtx(()=>[e.renderSlot(a.$slots,"default",{},()=>[e.createTextVNode(e.toDisplayString(t.text),1)],!0)]),_:3},8,["icon-definition","size","icon-only"]),t.chevron?(e.openBlock(),e.createBlock(e.unref(h.Icon),{key:0,"icon-definition":e.unref(z.faChevronDown),size:"xxsmall",inline:"",decorative:"",class:"phila-filter-chip__chevron"},null,8,["icon-definition"])):t.trailingIconDefinition?(e.openBlock(),e.createBlock(e.unref(h.Icon),{key:1,"icon-definition":t.trailingIconDefinition,size:w,inline:"",decorative:""},null,8,["icon-definition"])):e.createCommentVNode("",!0)],10,F))}}),A=(t,m)=>{const l=t.__vccOpts||t;for(const[r,i]of m)l[r]=i;return l},S=A(R,[["__scopeId","data-v-7411a108"]]),L=["id","aria-label"],U={class:"phila-filter-chip-panel__footer"},O=e.defineComponent({__name:"FilterChipDropdownPanel",props:{menuId:{},label:{},choices:{},multiple:{type:Boolean},value:{},top:{},left:{},applyText:{default:"Apply"},resetText:{default:"Reset"}},emits:["apply","reset"],setup(t,{emit:m}){const l=t,r=m,i=e.ref(typeof l.value=="string"?l.value:""),u=e.ref(Array.isArray(l.value)?[...l.value]:[]);e.watch(()=>l.value,d=>{l.multiple?u.value=Array.isArray(d)?[...d]:[]:i.value=typeof d=="string"?d:""});const a=e.computed(()=>l.choices.map(d=>({text:d.text,value:d.value}))),y=()=>{i.value="",u.value=[],r("reset")},v=()=>{r("apply",l.multiple?[...u.value]:i.value)};return(d,c)=>(e.openBlock(),e.createBlock(e.Teleport,{to:"body"},[e.createElementVNode("div",{id:t.menuId,class:"phila-filter-chip-panel",role:"dialog","aria-label":t.label,style:e.normalizeStyle({top:t.top+"px",left:t.left+"px"})},[t.multiple?(e.openBlock(),e.createBlock(e.unref(E.CheckboxGroup),{key:0,"group-label":t.label,choices:a.value,"model-value":u.value,"onUpdate:modelValue":c[0]||(c[0]=f=>u.value=f)},null,8,["group-label","choices","model-value"])):(e.openBlock(),e.createBlock(e.unref(T.RadioGroup),{key:1,"group-label":t.label,choices:a.value,"model-value":i.value,"onUpdate:modelValue":c[1]||(c[1]=f=>i.value=f)},null,8,["group-label","choices","model-value"])),e.createElementVNode("div",U,[e.createElementVNode("button",{type:"button",class:"phila-filter-chip-panel__reset has-text-body-small",onClick:y},e.toDisplayString(t.resetText),1),e.createVNode(e.unref(P.PhilaButton),{variant:"primary",size:"small",text:t.applyText,onClick:v},null,8,["text"])])],12,L)]))}});let q=0;const I=e.defineComponent({__name:"FilterChip",props:{label:{default:void 0},text:{default:void 0},size:{default:"medium"},color:{default:"blue"},iconDefinition:{default:void 0},trailingIconDefinition:{default:void 0},selected:{type:Boolean,default:!1},choices:{default:void 0},multiple:{type:Boolean,default:!1},modelValue:{type:[String,Array,Boolean],default:void 0},applyText:{default:"Apply"},resetText:{default:"Reset"},className:{}},emits:["update:selected","update:modelValue","click"],setup(t,{emit:m}){const l=t,r=m,i=e.computed(()=>Array.isArray(l.choices)),u=e.useSlots(),a=e.computed(()=>!i.value&&!!l.iconDefinition&&!l.text&&!l.label&&!u.default);function y(p){r("update:selected",!l.selected),r("click",p)}const v=`phila-filter-chip-${e.useId()}-${++q}`,{isVisible:d,setVisibility:c}=h.useVisibility({id:v,group:"phila-filter-chip",outsideClickHide:!0,escapeKeyHide:!0,showSingle:!0}),f=e.computed(()=>d(v)),C=e.ref(null),x=e.ref(0),b=e.ref(0),g=e.computed(()=>l.multiple?Array.isArray(l.modelValue)?l.modelValue:[]:typeof l.modelValue=="string"?l.modelValue:""),V=e.computed(()=>l.multiple?Array.isArray(l.modelValue)&&l.modelValue.length>0:typeof l.modelValue=="string"&&l.modelValue!==""),k=e.computed(()=>l.multiple&&Array.isArray(l.modelValue)?l.modelValue.length:0),n=e.computed(()=>{const p=l.label??l.text??"";return k.value>0?`${p} (${k.value})`:p});function s(){const p=C.value?.el??null;if(!p)return;const D=p.getBoundingClientRect();x.value=D.bottom+4,b.value=D.left}function o(){f.value&&c(!1)}e.watch(f,p=>{p?window.addEventListener("scroll",o,{capture:!0}):window.removeEventListener("scroll",o,{capture:!0})});function B(){const p=!f.value;p&&s(),c(p)}function N(p){r("update:modelValue",p),c(!1)}function $(){r("update:modelValue",l.multiple?[]:"")}return(p,D)=>i.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createVNode(S,{ref_key:"triggerRef",ref:C,text:n.value,size:t.size,color:t.color,"icon-definition":t.iconDefinition,active:V.value||f.value,chevron:"","aria-expanded":f.value,"data-toggle":`visibility-${v}`,onClick:B},null,8,["text","size","color","icon-definition","active","aria-expanded","data-toggle"]),f.value?(e.openBlock(),e.createBlock(O,{key:0,"menu-id":v,label:t.label??t.text??"",choices:t.choices,multiple:t.multiple,value:g.value,top:x.value,left:b.value,"apply-text":t.applyText,"reset-text":t.resetText,onApply:N,onReset:$},null,8,["label","choices","multiple","value","top","left","apply-text","reset-text"])):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createBlock(S,{key:1,size:t.size,color:t.color,"icon-definition":t.iconDefinition,"trailing-icon-definition":t.trailingIconDefinition,"icon-only":a.value,active:t.selected,"aria-pressed":t.selected,onClick:y},{default:e.withCtx(()=>[e.renderSlot(p.$slots,"default",{},()=>[e.createTextVNode(e.toDisplayString(t.text),1)])]),_:3},8,["size","color","icon-definition","trailing-icon-definition","icon-only","active","aria-pressed"]))}}),G=5;function H(){const t=e.ref(null),m=e.ref(!1);let l=!1,r=0,i=0,u=!1,a=null;const y=c=>{t.value&&c.pointerType!=="mouse"&&(l=!0,u=!1,r=c.clientX,i=t.value.scrollLeft)},v=c=>{if(!l||!t.value)return;const f=c.clientX-r;!u&&Math.abs(f)>G&&(u=!0,m.value=!0,t.value.setPointerCapture(c.pointerId),a=c.pointerId),u&&(t.value.scrollLeft=i-f)},d=()=>{if(t.value&&a!==null){try{t.value.releasePointerCapture(a)}catch{}a=null}l=!1,u&&setTimeout(()=>m.value=!1,0)};return e.onBeforeUnmount(d),{el:t,suppressClick:m,dragHandlers:{onPointerdown:y,onPointermove:v,onPointerup:d,onPointerleave:d}}}const M={key:0,class:"phila-filter-chip-group__label has-text-label-default"},W={class:"phila-filter-chip-group__viewport"},X=e.defineComponent({__name:"FilterChipGroup",props:{filters:{},modelValue:{default:()=>({})},label:{},size:{default:"medium"},color:{default:"blue"},filterButton:{type:Boolean,default:!1},className:{}},emits:["update:modelValue","open-filters"],setup(t,{emit:m}){const l=t,r=m,i=e.computed(()=>{let n=0;for(const s of l.filters){if(s.excludeFromCount)continue;const o=l.modelValue[s.key];Array.isArray(o)?n+=o.length:typeof o=="string"?n+=o?1:0:o===!0&&(n+=1)}return n}),u=e.computed(()=>i.value>0?`Filters (${i.value} selected)`:"Filters"),{el:a,suppressClick:y,dragHandlers:v}=H(),d=e.computed(()=>h.cn("phila-filter-chip-group",l.className));function c(n){return Array.isArray(n.choices)}function f(n,s){r("update:modelValue",{...l.modelValue,[n]:s})}function C(n){y.value&&(n.stopPropagation(),n.preventDefault())}const x=e.ref(!1),b=e.ref(!1);function g(){const n=a.value;n&&(x.value=n.scrollLeft>1,b.value=n.scrollLeft+n.clientWidth<n.scrollWidth-1)}function V(n){const s=a.value;s&&s.scrollBy({left:n*s.clientWidth*.8,behavior:"smooth"})}let k=null;return e.onMounted(()=>{e.nextTick(g),a.value&&(k=new ResizeObserver(()=>g()),k.observe(a.value))}),e.onBeforeUnmount(()=>{k?.disconnect(),k=null}),e.watch(()=>l.filters,()=>e.nextTick(g)),(n,s)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(d.value)},[t.label?(e.openBlock(),e.createElementBlock("span",M,e.toDisplayString(t.label),1)):e.createCommentVNode("",!0),e.createElementVNode("div",W,[x.value?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"phila-filter-chip-group__arrow phila-filter-chip-group__arrow--left","aria-label":"Scroll filters left",onClick:s[0]||(s[0]=o=>V(-1))},[e.createVNode(e.unref(h.Icon),{"icon-definition":e.unref(z.faChevronLeft),size:"xxsmall",inline:"",decorative:""},null,8,["icon-definition"])])):e.createCommentVNode("",!0),e.createElementVNode("div",e.mergeProps({ref_key:"el",ref:a,class:"phila-filter-chip-group__row"},e.unref(v),{onClickCapture:C,onScroll:g}),[t.filterButton?(e.openBlock(),e.createBlock(S,{key:0,size:t.size,color:t.color,"icon-definition":e.unref(z.faSliders),text:i.value>0?`(${i.value})`:"","icon-only":i.value===0,active:i.value>0,"aria-label":u.value,onClick:s[1]||(s[1]=o=>r("open-filters"))},null,8,["size","color","icon-definition","text","icon-only","active","aria-label"])):e.createCommentVNode("",!0),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.filters,o=>(e.openBlock(),e.createBlock(I,{key:o.key,label:o.label,text:o.label,size:t.size,color:t.color,"icon-definition":o.iconDefinition,choices:o.choices,multiple:o.multiple,"model-value":c(o)?t.modelValue[o.key]:void 0,selected:!c(o)&&t.modelValue[o.key]===!0,"onUpdate:modelValue":B=>f(o.key,B),"onUpdate:selected":B=>f(o.key,B)},null,8,["label","text","size","color","icon-definition","choices","multiple","model-value","selected","onUpdate:modelValue","onUpdate:selected"]))),128))],16),b.value?(e.openBlock(),e.createElementBlock("button",{key:1,type:"button",class:"phila-filter-chip-group__arrow phila-filter-chip-group__arrow--right","aria-label":"Scroll filters right",onClick:s[2]||(s[2]=o=>V(1))},[e.createVNode(e.unref(h.Icon),{"icon-definition":e.unref(z.faChevronRight),size:"xxsmall",inline:"",decorative:""},null,8,["icon-definition"])])):e.createCommentVNode("",!0)])],2))}}),j=A(X,[["__scopeId","data-v-99d3ffdf"]]);exports.FilterChip=I;exports.FilterChipGroup=j;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { defineComponent as T, ref as x, computed as h, createElementBlock as C, openBlock as p, normalizeClass as U, createVNode as S, createBlock as b, createCommentVNode as z, unref as m, withCtx as H, renderSlot as G, createTextVNode as M, toDisplayString as L, watch as E, Teleport as Q, createElementVNode as B, normalizeStyle as Y, useSlots as Z, useId as _, Fragment as W, onBeforeUnmount as X, onMounted as ee, nextTick as O, mergeProps as le, renderList as te } from "vue";
|
|
2
|
+
import { cn as K, ActionContent as ie, Icon as P, useVisibility as oe } from "@phila/phila-ui-core";
|
|
3
|
+
import { faChevronDown as ne, faChevronLeft as ae, faSliders as re, faChevronRight as ue } from "@fortawesome/pro-solid-svg-icons";
|
|
4
|
+
import { RadioGroup as ce } from "@phila/phila-ui-radio";
|
|
5
|
+
import { CheckboxGroup as se } from "@phila/phila-ui-checkbox";
|
|
6
|
+
import { PhilaButton as de } from "@phila/phila-ui-button";
|
|
7
|
+
import './index.css';const fe = ["aria-pressed", "aria-expanded"], N = "xxsmall", pe = /* @__PURE__ */ T({
|
|
8
|
+
__name: "FilterChipBase",
|
|
9
|
+
props: {
|
|
10
|
+
text: { default: void 0 },
|
|
11
|
+
size: { default: "medium" },
|
|
12
|
+
color: { default: "blue" },
|
|
13
|
+
iconDefinition: { default: void 0 },
|
|
14
|
+
trailingIconDefinition: { default: void 0 },
|
|
15
|
+
iconOnly: { type: Boolean, default: !1 },
|
|
16
|
+
active: { type: Boolean, default: !1 },
|
|
17
|
+
chevron: { type: Boolean, default: !1 },
|
|
18
|
+
ariaPressed: { type: Boolean },
|
|
19
|
+
ariaExpanded: { type: Boolean }
|
|
20
|
+
},
|
|
21
|
+
emits: ["click"],
|
|
22
|
+
setup(e, { expose: v }) {
|
|
23
|
+
const l = e, a = x(null), i = h(
|
|
24
|
+
() => K(
|
|
25
|
+
"phila-filter-chip",
|
|
26
|
+
`phila-filter-chip--${l.color}`,
|
|
27
|
+
l.size !== "medium" && `is-${l.size}`,
|
|
28
|
+
"has-text-body-" + u(l.size),
|
|
29
|
+
l.active && "is-active",
|
|
30
|
+
l.iconOnly && "is-icon-only"
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
v({ el: a });
|
|
34
|
+
const u = (n) => {
|
|
35
|
+
switch (n) {
|
|
36
|
+
case "extra-large":
|
|
37
|
+
return "default";
|
|
38
|
+
case "large":
|
|
39
|
+
return "small";
|
|
40
|
+
default:
|
|
41
|
+
return "extra-small";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return (n, g) => (p(), C("button", {
|
|
45
|
+
ref_key: "rootEl",
|
|
46
|
+
ref: a,
|
|
47
|
+
type: "button",
|
|
48
|
+
class: U(i.value),
|
|
49
|
+
"aria-pressed": e.ariaPressed,
|
|
50
|
+
"aria-expanded": e.ariaExpanded,
|
|
51
|
+
onClick: g[0] || (g[0] = (y) => n.$emit("click", y))
|
|
52
|
+
}, [
|
|
53
|
+
S(m(ie), {
|
|
54
|
+
"icon-definition": e.iconDefinition,
|
|
55
|
+
size: e.size,
|
|
56
|
+
inline: "",
|
|
57
|
+
"icon-size": N,
|
|
58
|
+
"icon-only": e.iconOnly
|
|
59
|
+
}, {
|
|
60
|
+
default: H(() => [
|
|
61
|
+
G(n.$slots, "default", {}, () => [
|
|
62
|
+
M(L(e.text), 1)
|
|
63
|
+
], !0)
|
|
64
|
+
]),
|
|
65
|
+
_: 3
|
|
66
|
+
}, 8, ["icon-definition", "size", "icon-only"]),
|
|
67
|
+
e.chevron ? (p(), b(m(P), {
|
|
68
|
+
key: 0,
|
|
69
|
+
"icon-definition": m(ne),
|
|
70
|
+
size: "xxsmall",
|
|
71
|
+
inline: "",
|
|
72
|
+
decorative: "",
|
|
73
|
+
class: "phila-filter-chip__chevron"
|
|
74
|
+
}, null, 8, ["icon-definition"])) : e.trailingIconDefinition ? (p(), b(m(P), {
|
|
75
|
+
key: 1,
|
|
76
|
+
"icon-definition": e.trailingIconDefinition,
|
|
77
|
+
size: N,
|
|
78
|
+
inline: "",
|
|
79
|
+
decorative: ""
|
|
80
|
+
}, null, 8, ["icon-definition"])) : z("", !0)
|
|
81
|
+
], 10, fe));
|
|
82
|
+
}
|
|
83
|
+
}), j = (e, v) => {
|
|
84
|
+
const l = e.__vccOpts || e;
|
|
85
|
+
for (const [a, i] of v)
|
|
86
|
+
l[a] = i;
|
|
87
|
+
return l;
|
|
88
|
+
}, F = /* @__PURE__ */ j(pe, [["__scopeId", "data-v-7411a108"]]), ve = ["id", "aria-label"], me = { class: "phila-filter-chip-panel__footer" }, ye = /* @__PURE__ */ T({
|
|
89
|
+
__name: "FilterChipDropdownPanel",
|
|
90
|
+
props: {
|
|
91
|
+
menuId: {},
|
|
92
|
+
label: {},
|
|
93
|
+
choices: {},
|
|
94
|
+
multiple: { type: Boolean },
|
|
95
|
+
value: {},
|
|
96
|
+
top: {},
|
|
97
|
+
left: {},
|
|
98
|
+
applyText: { default: "Apply" },
|
|
99
|
+
resetText: { default: "Reset" }
|
|
100
|
+
},
|
|
101
|
+
emits: ["apply", "reset"],
|
|
102
|
+
setup(e, { emit: v }) {
|
|
103
|
+
const l = e, a = v, i = x(typeof l.value == "string" ? l.value : ""), u = x(Array.isArray(l.value) ? [...l.value] : []);
|
|
104
|
+
E(
|
|
105
|
+
() => l.value,
|
|
106
|
+
(s) => {
|
|
107
|
+
l.multiple ? u.value = Array.isArray(s) ? [...s] : [] : i.value = typeof s == "string" ? s : "";
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
const n = h(() => l.choices.map((s) => ({ text: s.text, value: s.value }))), g = () => {
|
|
111
|
+
i.value = "", u.value = [], a("reset");
|
|
112
|
+
}, y = () => {
|
|
113
|
+
a("apply", l.multiple ? [...u.value] : i.value);
|
|
114
|
+
};
|
|
115
|
+
return (s, r) => (p(), b(Q, { to: "body" }, [
|
|
116
|
+
B("div", {
|
|
117
|
+
id: e.menuId,
|
|
118
|
+
class: "phila-filter-chip-panel",
|
|
119
|
+
role: "dialog",
|
|
120
|
+
"aria-label": e.label,
|
|
121
|
+
style: Y({ top: e.top + "px", left: e.left + "px" })
|
|
122
|
+
}, [
|
|
123
|
+
e.multiple ? (p(), b(m(se), {
|
|
124
|
+
key: 0,
|
|
125
|
+
"group-label": e.label,
|
|
126
|
+
choices: n.value,
|
|
127
|
+
"model-value": u.value,
|
|
128
|
+
"onUpdate:modelValue": r[0] || (r[0] = (d) => u.value = d)
|
|
129
|
+
}, null, 8, ["group-label", "choices", "model-value"])) : (p(), b(m(ce), {
|
|
130
|
+
key: 1,
|
|
131
|
+
"group-label": e.label,
|
|
132
|
+
choices: n.value,
|
|
133
|
+
"model-value": i.value,
|
|
134
|
+
"onUpdate:modelValue": r[1] || (r[1] = (d) => i.value = d)
|
|
135
|
+
}, null, 8, ["group-label", "choices", "model-value"])),
|
|
136
|
+
B("div", me, [
|
|
137
|
+
B("button", {
|
|
138
|
+
type: "button",
|
|
139
|
+
class: "phila-filter-chip-panel__reset has-text-body-small",
|
|
140
|
+
onClick: g
|
|
141
|
+
}, L(e.resetText), 1),
|
|
142
|
+
S(m(de), {
|
|
143
|
+
variant: "primary",
|
|
144
|
+
size: "small",
|
|
145
|
+
text: e.applyText,
|
|
146
|
+
onClick: y
|
|
147
|
+
}, null, 8, ["text"])
|
|
148
|
+
])
|
|
149
|
+
], 12, ve)
|
|
150
|
+
]));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
let he = 0;
|
|
154
|
+
const ge = /* @__PURE__ */ T({
|
|
155
|
+
__name: "FilterChip",
|
|
156
|
+
props: {
|
|
157
|
+
label: { default: void 0 },
|
|
158
|
+
text: { default: void 0 },
|
|
159
|
+
size: { default: "medium" },
|
|
160
|
+
color: { default: "blue" },
|
|
161
|
+
iconDefinition: { default: void 0 },
|
|
162
|
+
trailingIconDefinition: { default: void 0 },
|
|
163
|
+
selected: { type: Boolean, default: !1 },
|
|
164
|
+
choices: { default: void 0 },
|
|
165
|
+
multiple: { type: Boolean, default: !1 },
|
|
166
|
+
modelValue: { type: [String, Array, Boolean], default: void 0 },
|
|
167
|
+
applyText: { default: "Apply" },
|
|
168
|
+
resetText: { default: "Reset" },
|
|
169
|
+
className: {}
|
|
170
|
+
},
|
|
171
|
+
emits: ["update:selected", "update:modelValue", "click"],
|
|
172
|
+
setup(e, { emit: v }) {
|
|
173
|
+
const l = e, a = v, i = h(() => Array.isArray(l.choices)), u = Z(), n = h(
|
|
174
|
+
() => !i.value && !!l.iconDefinition && !l.text && !l.label && !u.default
|
|
175
|
+
);
|
|
176
|
+
function g(f) {
|
|
177
|
+
a("update:selected", !l.selected), a("click", f);
|
|
178
|
+
}
|
|
179
|
+
const y = `phila-filter-chip-${_()}-${++he}`, { isVisible: s, setVisibility: r } = oe({
|
|
180
|
+
id: y,
|
|
181
|
+
group: "phila-filter-chip",
|
|
182
|
+
outsideClickHide: !0,
|
|
183
|
+
escapeKeyHide: !0,
|
|
184
|
+
showSingle: !0
|
|
185
|
+
}), d = h(() => s(y)), $ = x(null), D = x(0), w = x(0), V = h(() => l.multiple ? Array.isArray(l.modelValue) ? l.modelValue : [] : typeof l.modelValue == "string" ? l.modelValue : ""), I = h(() => l.multiple ? Array.isArray(l.modelValue) && l.modelValue.length > 0 : typeof l.modelValue == "string" && l.modelValue !== ""), k = h(() => l.multiple && Array.isArray(l.modelValue) ? l.modelValue.length : 0), o = h(() => {
|
|
186
|
+
const f = l.label ?? l.text ?? "";
|
|
187
|
+
return k.value > 0 ? `${f} (${k.value})` : f;
|
|
188
|
+
});
|
|
189
|
+
function c() {
|
|
190
|
+
const f = $.value?.el ?? null;
|
|
191
|
+
if (!f) return;
|
|
192
|
+
const R = f.getBoundingClientRect();
|
|
193
|
+
D.value = R.bottom + 4, w.value = R.left;
|
|
194
|
+
}
|
|
195
|
+
function t() {
|
|
196
|
+
d.value && r(!1);
|
|
197
|
+
}
|
|
198
|
+
E(d, (f) => {
|
|
199
|
+
f ? window.addEventListener("scroll", t, { capture: !0 }) : window.removeEventListener("scroll", t, { capture: !0 });
|
|
200
|
+
});
|
|
201
|
+
function A() {
|
|
202
|
+
const f = !d.value;
|
|
203
|
+
f && c(), r(f);
|
|
204
|
+
}
|
|
205
|
+
function q(f) {
|
|
206
|
+
a("update:modelValue", f), r(!1);
|
|
207
|
+
}
|
|
208
|
+
function J() {
|
|
209
|
+
a("update:modelValue", l.multiple ? [] : "");
|
|
210
|
+
}
|
|
211
|
+
return (f, R) => i.value ? (p(), C(W, { key: 0 }, [
|
|
212
|
+
S(F, {
|
|
213
|
+
ref_key: "triggerRef",
|
|
214
|
+
ref: $,
|
|
215
|
+
text: o.value,
|
|
216
|
+
size: e.size,
|
|
217
|
+
color: e.color,
|
|
218
|
+
"icon-definition": e.iconDefinition,
|
|
219
|
+
active: I.value || d.value,
|
|
220
|
+
chevron: "",
|
|
221
|
+
"aria-expanded": d.value,
|
|
222
|
+
"data-toggle": `visibility-${y}`,
|
|
223
|
+
onClick: A
|
|
224
|
+
}, null, 8, ["text", "size", "color", "icon-definition", "active", "aria-expanded", "data-toggle"]),
|
|
225
|
+
d.value ? (p(), b(ye, {
|
|
226
|
+
key: 0,
|
|
227
|
+
"menu-id": y,
|
|
228
|
+
label: e.label ?? e.text ?? "",
|
|
229
|
+
choices: e.choices,
|
|
230
|
+
multiple: e.multiple,
|
|
231
|
+
value: V.value,
|
|
232
|
+
top: D.value,
|
|
233
|
+
left: w.value,
|
|
234
|
+
"apply-text": e.applyText,
|
|
235
|
+
"reset-text": e.resetText,
|
|
236
|
+
onApply: q,
|
|
237
|
+
onReset: J
|
|
238
|
+
}, null, 8, ["label", "choices", "multiple", "value", "top", "left", "apply-text", "reset-text"])) : z("", !0)
|
|
239
|
+
], 64)) : (p(), b(F, {
|
|
240
|
+
key: 1,
|
|
241
|
+
size: e.size,
|
|
242
|
+
color: e.color,
|
|
243
|
+
"icon-definition": e.iconDefinition,
|
|
244
|
+
"trailing-icon-definition": e.trailingIconDefinition,
|
|
245
|
+
"icon-only": n.value,
|
|
246
|
+
active: e.selected,
|
|
247
|
+
"aria-pressed": e.selected,
|
|
248
|
+
onClick: g
|
|
249
|
+
}, {
|
|
250
|
+
default: H(() => [
|
|
251
|
+
G(f.$slots, "default", {}, () => [
|
|
252
|
+
M(L(e.text), 1)
|
|
253
|
+
])
|
|
254
|
+
]),
|
|
255
|
+
_: 3
|
|
256
|
+
}, 8, ["size", "color", "icon-definition", "trailing-icon-definition", "icon-only", "active", "aria-pressed"]));
|
|
257
|
+
}
|
|
258
|
+
}), xe = 5;
|
|
259
|
+
function be() {
|
|
260
|
+
const e = x(null), v = x(!1);
|
|
261
|
+
let l = !1, a = 0, i = 0, u = !1, n = null;
|
|
262
|
+
const g = (r) => {
|
|
263
|
+
e.value && r.pointerType !== "mouse" && (l = !0, u = !1, a = r.clientX, i = e.value.scrollLeft);
|
|
264
|
+
}, y = (r) => {
|
|
265
|
+
if (!l || !e.value) return;
|
|
266
|
+
const d = r.clientX - a;
|
|
267
|
+
!u && Math.abs(d) > xe && (u = !0, v.value = !0, e.value.setPointerCapture(r.pointerId), n = r.pointerId), u && (e.value.scrollLeft = i - d);
|
|
268
|
+
}, s = () => {
|
|
269
|
+
if (e.value && n !== null) {
|
|
270
|
+
try {
|
|
271
|
+
e.value.releasePointerCapture(n);
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
n = null;
|
|
275
|
+
}
|
|
276
|
+
l = !1, u && setTimeout(() => v.value = !1, 0);
|
|
277
|
+
};
|
|
278
|
+
return X(s), {
|
|
279
|
+
el: e,
|
|
280
|
+
suppressClick: v,
|
|
281
|
+
dragHandlers: {
|
|
282
|
+
onPointerdown: g,
|
|
283
|
+
onPointermove: y,
|
|
284
|
+
onPointerup: s,
|
|
285
|
+
onPointerleave: s
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const ke = {
|
|
290
|
+
key: 0,
|
|
291
|
+
class: "phila-filter-chip-group__label has-text-label-default"
|
|
292
|
+
}, Ce = { class: "phila-filter-chip-group__viewport" }, Ve = /* @__PURE__ */ T({
|
|
293
|
+
__name: "FilterChipGroup",
|
|
294
|
+
props: {
|
|
295
|
+
filters: {},
|
|
296
|
+
modelValue: { default: () => ({}) },
|
|
297
|
+
label: {},
|
|
298
|
+
size: { default: "medium" },
|
|
299
|
+
color: { default: "blue" },
|
|
300
|
+
filterButton: { type: Boolean, default: !1 },
|
|
301
|
+
className: {}
|
|
302
|
+
},
|
|
303
|
+
emits: ["update:modelValue", "open-filters"],
|
|
304
|
+
setup(e, { emit: v }) {
|
|
305
|
+
const l = e, a = v, i = h(() => {
|
|
306
|
+
let o = 0;
|
|
307
|
+
for (const c of l.filters) {
|
|
308
|
+
if (c.excludeFromCount) continue;
|
|
309
|
+
const t = l.modelValue[c.key];
|
|
310
|
+
Array.isArray(t) ? o += t.length : typeof t == "string" ? o += t ? 1 : 0 : t === !0 && (o += 1);
|
|
311
|
+
}
|
|
312
|
+
return o;
|
|
313
|
+
}), u = h(
|
|
314
|
+
() => i.value > 0 ? `Filters (${i.value} selected)` : "Filters"
|
|
315
|
+
), { el: n, suppressClick: g, dragHandlers: y } = be(), s = h(() => K("phila-filter-chip-group", l.className));
|
|
316
|
+
function r(o) {
|
|
317
|
+
return Array.isArray(o.choices);
|
|
318
|
+
}
|
|
319
|
+
function d(o, c) {
|
|
320
|
+
a("update:modelValue", { ...l.modelValue, [o]: c });
|
|
321
|
+
}
|
|
322
|
+
function $(o) {
|
|
323
|
+
g.value && (o.stopPropagation(), o.preventDefault());
|
|
324
|
+
}
|
|
325
|
+
const D = x(!1), w = x(!1);
|
|
326
|
+
function V() {
|
|
327
|
+
const o = n.value;
|
|
328
|
+
o && (D.value = o.scrollLeft > 1, w.value = o.scrollLeft + o.clientWidth < o.scrollWidth - 1);
|
|
329
|
+
}
|
|
330
|
+
function I(o) {
|
|
331
|
+
const c = n.value;
|
|
332
|
+
c && c.scrollBy({ left: o * c.clientWidth * 0.8, behavior: "smooth" });
|
|
333
|
+
}
|
|
334
|
+
let k = null;
|
|
335
|
+
return ee(() => {
|
|
336
|
+
O(V), n.value && (k = new ResizeObserver(() => V()), k.observe(n.value));
|
|
337
|
+
}), X(() => {
|
|
338
|
+
k?.disconnect(), k = null;
|
|
339
|
+
}), E(
|
|
340
|
+
() => l.filters,
|
|
341
|
+
() => O(V)
|
|
342
|
+
), (o, c) => (p(), C("div", {
|
|
343
|
+
class: U(s.value)
|
|
344
|
+
}, [
|
|
345
|
+
e.label ? (p(), C("span", ke, L(e.label), 1)) : z("", !0),
|
|
346
|
+
B("div", Ce, [
|
|
347
|
+
D.value ? (p(), C("button", {
|
|
348
|
+
key: 0,
|
|
349
|
+
type: "button",
|
|
350
|
+
class: "phila-filter-chip-group__arrow phila-filter-chip-group__arrow--left",
|
|
351
|
+
"aria-label": "Scroll filters left",
|
|
352
|
+
onClick: c[0] || (c[0] = (t) => I(-1))
|
|
353
|
+
}, [
|
|
354
|
+
S(m(P), {
|
|
355
|
+
"icon-definition": m(ae),
|
|
356
|
+
size: "xxsmall",
|
|
357
|
+
inline: "",
|
|
358
|
+
decorative: ""
|
|
359
|
+
}, null, 8, ["icon-definition"])
|
|
360
|
+
])) : z("", !0),
|
|
361
|
+
B("div", le({
|
|
362
|
+
ref_key: "el",
|
|
363
|
+
ref: n,
|
|
364
|
+
class: "phila-filter-chip-group__row"
|
|
365
|
+
}, m(y), {
|
|
366
|
+
onClickCapture: $,
|
|
367
|
+
onScroll: V
|
|
368
|
+
}), [
|
|
369
|
+
e.filterButton ? (p(), b(F, {
|
|
370
|
+
key: 0,
|
|
371
|
+
size: e.size,
|
|
372
|
+
color: e.color,
|
|
373
|
+
"icon-definition": m(re),
|
|
374
|
+
text: i.value > 0 ? `(${i.value})` : "",
|
|
375
|
+
"icon-only": i.value === 0,
|
|
376
|
+
active: i.value > 0,
|
|
377
|
+
"aria-label": u.value,
|
|
378
|
+
onClick: c[1] || (c[1] = (t) => a("open-filters"))
|
|
379
|
+
}, null, 8, ["size", "color", "icon-definition", "text", "icon-only", "active", "aria-label"])) : z("", !0),
|
|
380
|
+
(p(!0), C(W, null, te(e.filters, (t) => (p(), b(ge, {
|
|
381
|
+
key: t.key,
|
|
382
|
+
label: t.label,
|
|
383
|
+
text: t.label,
|
|
384
|
+
size: e.size,
|
|
385
|
+
color: e.color,
|
|
386
|
+
"icon-definition": t.iconDefinition,
|
|
387
|
+
choices: t.choices,
|
|
388
|
+
multiple: t.multiple,
|
|
389
|
+
"model-value": r(t) ? e.modelValue[t.key] : void 0,
|
|
390
|
+
selected: !r(t) && e.modelValue[t.key] === !0,
|
|
391
|
+
"onUpdate:modelValue": (A) => d(t.key, A),
|
|
392
|
+
"onUpdate:selected": (A) => d(t.key, A)
|
|
393
|
+
}, null, 8, ["label", "text", "size", "color", "icon-definition", "choices", "multiple", "model-value", "selected", "onUpdate:modelValue", "onUpdate:selected"]))), 128))
|
|
394
|
+
], 16),
|
|
395
|
+
w.value ? (p(), C("button", {
|
|
396
|
+
key: 1,
|
|
397
|
+
type: "button",
|
|
398
|
+
class: "phila-filter-chip-group__arrow phila-filter-chip-group__arrow--right",
|
|
399
|
+
"aria-label": "Scroll filters right",
|
|
400
|
+
onClick: c[2] || (c[2] = (t) => I(1))
|
|
401
|
+
}, [
|
|
402
|
+
S(m(P), {
|
|
403
|
+
"icon-definition": m(ue),
|
|
404
|
+
size: "xxsmall",
|
|
405
|
+
inline: "",
|
|
406
|
+
decorative: ""
|
|
407
|
+
}, null, 8, ["icon-definition"])
|
|
408
|
+
])) : z("", !0)
|
|
409
|
+
])
|
|
410
|
+
], 2));
|
|
411
|
+
}
|
|
412
|
+
}), $e = /* @__PURE__ */ j(Ve, [["__scopeId", "data-v-99d3ffdf"]]);
|
|
413
|
+
export {
|
|
414
|
+
ge as FilterChip,
|
|
415
|
+
$e as FilterChipGroup
|
|
416
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pointer drag-to-scroll for a horizontally overflowing element.
|
|
3
|
+
* While dragging past DRAG_THRESHOLD, sets `suppressClick` true for the
|
|
4
|
+
* duration of the drag so a chip click does not fire on drag release.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useDragScroll(): {
|
|
7
|
+
el: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
8
|
+
suppressClick: import('vue').Ref<boolean, boolean>;
|
|
9
|
+
dragHandlers: {
|
|
10
|
+
onPointerdown: (e: PointerEvent) => void;
|
|
11
|
+
onPointermove: (e: PointerEvent) => void;
|
|
12
|
+
onPointerup: () => void;
|
|
13
|
+
onPointerleave: () => void;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=useDragScroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDragScroll.d.ts","sourceRoot":"","sources":["../src/useDragScroll.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,aAAa;;;;2BAUD,YAAY;2BAWZ,YAAY;;;;EAsCvC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@phila/phila-ui-filter-chip",
|
|
3
|
+
"version": "0.2.0-beta.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Filter chips for faceted filtering",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"ui",
|
|
21
|
+
"filter-chip",
|
|
22
|
+
"vue",
|
|
23
|
+
"component"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"vue": "^3.0.0",
|
|
29
|
+
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
|
30
|
+
"@fortawesome/pro-solid-svg-icons": "^7.1.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@phila/phila-ui-checkbox": "0.1.1-beta.4",
|
|
34
|
+
"@phila/phila-ui-core": "3.0.0-beta.3",
|
|
35
|
+
"@phila/phila-ui-radio": "0.1.1-beta.4",
|
|
36
|
+
"@phila/phila-ui-button": "2.2.3-beta.4"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^24.0.0",
|
|
40
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
41
|
+
"eslint": "^9.0.0",
|
|
42
|
+
"typescript": "^5.8.3",
|
|
43
|
+
"vite": "^7.0.6",
|
|
44
|
+
"vite-plugin-dts": "^4.5.4",
|
|
45
|
+
"vite-plugin-lib-inject-css": "^2.2.2"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "vite build",
|
|
49
|
+
"build-win": "vite build",
|
|
50
|
+
"dev": "vite build --watch",
|
|
51
|
+
"lint": "eslint src --ext .ts,.tsx,.vue",
|
|
52
|
+
"lint:fix": "eslint src --ext .ts,.tsx,.vue --fix",
|
|
53
|
+
"type-check": "tsc --noEmit",
|
|
54
|
+
"clean": "rm -rf dist",
|
|
55
|
+
"format": "prettier --write .",
|
|
56
|
+
"format:check": "prettier --check ."
|
|
57
|
+
}
|
|
58
|
+
}
|