@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 ADDED
@@ -0,0 +1,214 @@
1
+ # FilterChip Component
2
+
3
+ <!-- status-badge-start -->
4
+
5
+ ### Component Status
6
+
7
+ | Component | Status |
8
+ | --------------- | ------------------------------------------------------------------ |
9
+ | FilterChip | ![In Progress](https://img.shields.io/badge/-In%20Progress-yellow) |
10
+ | FilterChipGroup | ![In Progress](https://img.shields.io/badge/-In%20Progress-yellow) |
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}}
@@ -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
+ }