@squirkle-ui/icons 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +34 -2
- package/dist/styles.css +19 -0
- package/dist/types/SqIconPicker.vue.d.ts +2309 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/vue2/index.cjs +116 -0
- package/dist/vue2/index.cjs.map +1 -0
- package/dist/vue2/index.mjs +111 -0
- package/dist/vue2/index.mjs.map +1 -0
- package/dist/vue3/index.cjs +209 -0
- package/dist/vue3/index.cjs.map +1 -0
- package/dist/vue3/index.mjs +204 -0
- package/dist/vue3/index.mjs.map +1 -0
- package/dist/vue3/styles.css +19 -0
- package/package.json +60 -3
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { addCollection, addIcons } from '@squirkle-ui/ui';
|
|
2
|
+
import SqIconPicker from './SqIconPicker.vue';
|
|
3
|
+
export { SqIconPicker, addCollection, addIcons };
|
|
4
|
+
export declare const componentNames: readonly ["SqIconPicker"];
|
|
5
|
+
/** `$alias` presets mapping friendly names to popular Iconify sets. */
|
|
6
|
+
export declare const aliasPresets: {
|
|
7
|
+
readonly mdi: {
|
|
8
|
+
readonly home: "mdi:home-outline";
|
|
9
|
+
readonly user: "mdi:account-outline";
|
|
10
|
+
readonly settings: "mdi:cog-outline";
|
|
11
|
+
readonly bell: "mdi:bell-outline";
|
|
12
|
+
readonly heart: "mdi:heart-outline";
|
|
13
|
+
readonly cart: "mdi:cart-outline";
|
|
14
|
+
readonly chart: "mdi:chart-box-outline";
|
|
15
|
+
readonly mail: "mdi:email-outline";
|
|
16
|
+
readonly github: "mdi:github";
|
|
17
|
+
};
|
|
18
|
+
readonly lucide: {
|
|
19
|
+
readonly home: "lucide:home";
|
|
20
|
+
readonly user: "lucide:user";
|
|
21
|
+
readonly settings: "lucide:settings";
|
|
22
|
+
readonly bell: "lucide:bell";
|
|
23
|
+
readonly heart: "lucide:heart";
|
|
24
|
+
readonly cart: "lucide:shopping-cart";
|
|
25
|
+
readonly chart: "lucide:bar-chart-3";
|
|
26
|
+
readonly mail: "lucide:mail";
|
|
27
|
+
readonly github: "lucide:github";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** Registers a preset so `<SqIcon icon="$home">` resolves to that set. */
|
|
31
|
+
export declare function useIconPreset(name: keyof typeof aliasPresets): void;
|
|
32
|
+
/** Warms the icon cache (batched Iconify API request) before icons render. */
|
|
33
|
+
export declare function preloadIcons(names: string[]): void;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ui = require("@squirkle-ui/ui/vue2");
|
|
4
|
+
const vue = require("vue");
|
|
5
|
+
const core = require("@squirkle-ui/core");
|
|
6
|
+
const _sfc_main = vue.defineComponent({
|
|
7
|
+
name: "SqIconPicker",
|
|
8
|
+
components: { SqIcon: ui.SqIcon, SqProgressCircular: ui.SqProgressCircular, SqSelect: ui.SqSelect, SqShape: ui.SqShape, SqTextField: ui.SqTextField, SqVirtualScroll: ui.SqVirtualScroll },
|
|
9
|
+
model: ui.vModel,
|
|
10
|
+
props: {
|
|
11
|
+
modelValue: { type: String, default: void 0 },
|
|
12
|
+
/** Limit to icon sets, e.g. ['mdi', 'lucide', 'tabler']. */
|
|
13
|
+
prefixes: { type: Array, default: () => ["mdi", "lucide", "tabler", "ph", "solar"] },
|
|
14
|
+
columns: { type: Number, default: 8 },
|
|
15
|
+
height: { type: [Number, String], default: 280 },
|
|
16
|
+
limit: { type: Number, default: 400 },
|
|
17
|
+
placeholder: { type: String, default: "Search icons…" }
|
|
18
|
+
},
|
|
19
|
+
emits: ["update:modelValue"],
|
|
20
|
+
setup(props, { emit }) {
|
|
21
|
+
const model = ui.useProxiedModel(props, emit);
|
|
22
|
+
const query = vue.ref("home");
|
|
23
|
+
const prefix = vue.ref("all");
|
|
24
|
+
const results = vue.ref([]);
|
|
25
|
+
const loading = vue.ref(false);
|
|
26
|
+
const error = vue.ref("");
|
|
27
|
+
const prefixItems = vue.computed(() => [{ title: "All sets", value: "all" }, ...props.prefixes.map((p) => ({ title: p, value: p }))]);
|
|
28
|
+
let seq = 0;
|
|
29
|
+
const search = core.debounce(async () => {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
const q = ((_a = query.value) != null ? _a : "").trim();
|
|
32
|
+
const api = ui.config.icons.api;
|
|
33
|
+
if (!q || !api) return results.value = [];
|
|
34
|
+
const my = ++seq;
|
|
35
|
+
loading.value = true;
|
|
36
|
+
error.value = "";
|
|
37
|
+
try {
|
|
38
|
+
const sets = prefix.value === "all" ? props.prefixes.join(",") : prefix.value;
|
|
39
|
+
const res = await fetch(`${api}/search?query=${encodeURIComponent(q)}&limit=${props.limit}&prefixes=${encodeURIComponent(sets)}`);
|
|
40
|
+
const json = await res.json();
|
|
41
|
+
if (my === seq) results.value = (_b = json.icons) != null ? _b : [];
|
|
42
|
+
} catch {
|
|
43
|
+
if (my === seq) error.value = "Icon search is unavailable (offline?)";
|
|
44
|
+
} finally {
|
|
45
|
+
if (my === seq) loading.value = false;
|
|
46
|
+
}
|
|
47
|
+
}, 250);
|
|
48
|
+
vue.watch([query, prefix], () => search());
|
|
49
|
+
vue.onMounted(() => search());
|
|
50
|
+
const rows = vue.computed(() => {
|
|
51
|
+
const out = [];
|
|
52
|
+
for (let i = 0; i < results.value.length; i += props.columns) out.push(results.value.slice(i, i + props.columns));
|
|
53
|
+
return out;
|
|
54
|
+
});
|
|
55
|
+
return { model, query, prefix, prefixItems, results, rows, loading, error };
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
function normalizeComponent(scriptExports, render2, staticRenderFns, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) {
|
|
59
|
+
var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports;
|
|
60
|
+
if (render2) {
|
|
61
|
+
options.render = render2;
|
|
62
|
+
options.staticRenderFns = staticRenderFns;
|
|
63
|
+
options._compiled = true;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
exports: scriptExports,
|
|
67
|
+
options
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var _sfc_render = function render() {
|
|
71
|
+
var _vm = this, _c = _vm._self._c;
|
|
72
|
+
_vm._self._setupProxy;
|
|
73
|
+
return _c("div", { staticClass: "sq-icon-picker sq-surface", style: { "--sq-r": "var(--sq-radius-xl)", "--sq-bg": "var(--sq-surface-raised)", "--sq-bd": "var(--sq-border)", "--sq-bw": "1px" } }, [_c("SqShape"), _c("div", { staticClass: "sq-icon-picker__bar" }, [_c("SqTextField", { attrs: { "placeholder": _vm.placeholder, "prepend-inner-icon": "search", "density": "compact", "hide-details": "", "clearable": "" }, model: { value: _vm.query, callback: function($$v) {
|
|
74
|
+
_vm.query = $$v;
|
|
75
|
+
}, expression: "query" } }), _vm.prefixes.length > 1 ? _c("SqSelect", { staticClass: "sq-icon-picker__prefix", attrs: { "items": _vm.prefixItems, "density": "compact", "hide-details": "" }, model: { value: _vm.prefix, callback: function($$v) {
|
|
76
|
+
_vm.prefix = $$v;
|
|
77
|
+
}, expression: "prefix" } }) : _vm._e()], 1), _vm.loading ? _c("div", { staticClass: "sq-icon-picker__state" }, [_c("SqProgressCircular", { attrs: { "indeterminate": "", "size": 22, "width": 3 } })], 1) : _vm.error ? _c("div", { staticClass: "sq-icon-picker__state" }, [_vm._v(_vm._s(_vm.error))]) : !_vm.results.length ? _c("div", { staticClass: "sq-icon-picker__state" }, [_vm._v(_vm._s(_vm.query ? "No icons found" : "Type to search 200,000+ Iconify icons"))]) : _c("SqVirtualScroll", { attrs: { "items": _vm.rows, "item-height": 56, "height": _vm.height }, scopedSlots: _vm._u([{ key: "default", fn: function({ item }) {
|
|
78
|
+
return [_c("div", { staticClass: "sq-icon-picker__row" }, _vm._l(item, function(name) {
|
|
79
|
+
return _c("button", { key: name, staticClass: "sq-icon-picker__cell sq-squircle", class: { "sq-icon-picker__cell--active": name === _vm.model }, attrs: { "type": "button", "title": name, "aria-label": name, "aria-pressed": name === _vm.model ? "true" : "false" }, on: { "click": function($event) {
|
|
80
|
+
_vm.model = name;
|
|
81
|
+
} } }, [_c("SqIcon", { attrs: { "icon": name, "size": "24px" } })], 1);
|
|
82
|
+
}), 0)];
|
|
83
|
+
} }]) }), _vm.model ? _c("div", { staticClass: "sq-icon-picker__selected" }, [_c("SqIcon", { attrs: { "icon": _vm.model, "size": "20px" } }), _c("code", [_vm._v(_vm._s(_vm.model))])], 1) : _vm._e()], 1);
|
|
84
|
+
};
|
|
85
|
+
var _sfc_staticRenderFns = [];
|
|
86
|
+
var __component__ = /* @__PURE__ */ normalizeComponent(
|
|
87
|
+
_sfc_main,
|
|
88
|
+
_sfc_render,
|
|
89
|
+
_sfc_staticRenderFns
|
|
90
|
+
);
|
|
91
|
+
const SqIconPicker = __component__.exports;
|
|
92
|
+
const componentNames = ["SqIconPicker"];
|
|
93
|
+
const aliasPresets = {
|
|
94
|
+
mdi: { home: "mdi:home-outline", user: "mdi:account-outline", settings: "mdi:cog-outline", bell: "mdi:bell-outline", heart: "mdi:heart-outline", cart: "mdi:cart-outline", chart: "mdi:chart-box-outline", mail: "mdi:email-outline", github: "mdi:github" },
|
|
95
|
+
lucide: { home: "lucide:home", user: "lucide:user", settings: "lucide:settings", bell: "lucide:bell", heart: "lucide:heart", cart: "lucide:shopping-cart", chart: "lucide:bar-chart-3", mail: "lucide:mail", github: "lucide:github" }
|
|
96
|
+
};
|
|
97
|
+
function useIconPreset(name) {
|
|
98
|
+
ui.applyOptions({ icons: { aliases: { ...aliasPresets[name] } } });
|
|
99
|
+
}
|
|
100
|
+
function preloadIcons(names) {
|
|
101
|
+
names.forEach((n) => ui.resolveIcon(n));
|
|
102
|
+
}
|
|
103
|
+
Object.defineProperty(exports, "addCollection", {
|
|
104
|
+
enumerable: true,
|
|
105
|
+
get: () => ui.addCollection
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(exports, "addIcons", {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
get: () => ui.addIcons
|
|
110
|
+
});
|
|
111
|
+
exports.SqIconPicker = SqIconPicker;
|
|
112
|
+
exports.aliasPresets = aliasPresets;
|
|
113
|
+
exports.componentNames = componentNames;
|
|
114
|
+
exports.preloadIcons = preloadIcons;
|
|
115
|
+
exports.useIconPreset = useIconPreset;
|
|
116
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/SqIconPicker.vue","../../src/index.ts"],"sourcesContent":["<template>\n <div class=\"sq-icon-picker sq-surface\" :style=\"{ '--sq-r': 'var(--sq-radius-xl)', '--sq-bg': 'var(--sq-surface-raised)', '--sq-bd': 'var(--sq-border)', '--sq-bw': '1px' }\">\n <SqShape />\n <div class=\"sq-icon-picker__bar\">\n <SqTextField v-model=\"query\" :placeholder=\"placeholder\" prepend-inner-icon=\"search\" density=\"compact\" hide-details clearable />\n <SqSelect v-if=\"prefixes.length > 1\" v-model=\"prefix\" :items=\"prefixItems\" density=\"compact\" hide-details class=\"sq-icon-picker__prefix\" />\n </div>\n <div v-if=\"loading\" class=\"sq-icon-picker__state\"><SqProgressCircular indeterminate :size=\"22\" :width=\"3\" /></div>\n <div v-else-if=\"error\" class=\"sq-icon-picker__state\">{{ error }}</div>\n <div v-else-if=\"!results.length\" class=\"sq-icon-picker__state\">{{ query ? 'No icons found' : 'Type to search 200,000+ Iconify icons' }}</div>\n <SqVirtualScroll v-else :items=\"rows\" :item-height=\"56\" :height=\"height\">\n <template #default=\"{ item }\">\n <div class=\"sq-icon-picker__row\">\n <button\n v-for=\"name in item\"\n :key=\"name\"\n type=\"button\"\n class=\"sq-icon-picker__cell sq-squircle\"\n :class=\"{ 'sq-icon-picker__cell--active': name === model }\"\n :title=\"name\"\n :aria-label=\"name\"\n :aria-pressed=\"name === model ? 'true' : 'false'\"\n @click=\"model = name\"\n ><SqIcon :icon=\"name\" size=\"24px\" /></button>\n </div>\n </template>\n </SqVirtualScroll>\n <div v-if=\"model\" class=\"sq-icon-picker__selected\"><SqIcon :icon=\"model\" size=\"20px\" /><code>{{ model }}</code></div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, onMounted, ref, watch, type PropType } from 'vue'\nimport { debounce } from '@squirkle-ui/core'\nimport { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll, config, useProxiedModel, vModel } from '@squirkle-ui/ui'\n\n/** Searchable Iconify browser (uses the configured Iconify API host). v-model = icon name, e.g. `mdi:home`. */\nexport default defineComponent({\n name: 'SqIconPicker',\n components: { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll },\n model: vModel,\n props: {\n modelValue: { type: String, default: undefined },\n /** Limit to icon sets, e.g. ['mdi', 'lucide', 'tabler']. */\n prefixes: { type: Array as PropType<string[]>, default: () => ['mdi', 'lucide', 'tabler', 'ph', 'solar'] },\n columns: { type: Number, default: 8 },\n height: { type: [Number, String], default: 280 },\n limit: { type: Number, default: 400 },\n placeholder: { type: String, default: 'Search icons…' },\n },\n emits: ['update:modelValue'],\n setup(props, { emit }) {\n const model = useProxiedModel<string | undefined>(props, emit)\n const query = ref('home')\n const prefix = ref('all')\n const results = ref<string[]>([])\n const loading = ref(false)\n const error = ref('')\n const prefixItems = computed(() => [{ title: 'All sets', value: 'all' }, ...props.prefixes.map((p) => ({ title: p, value: p }))])\n let seq = 0\n const search = debounce(async () => {\n const q = (query.value ?? '').trim()\n const api = config.icons.api\n if (!q || !api) return (results.value = [])\n const my = ++seq\n loading.value = true\n error.value = ''\n try {\n const sets = prefix.value === 'all' ? props.prefixes.join(',') : prefix.value\n const res = await fetch(`${api}/search?query=${encodeURIComponent(q)}&limit=${props.limit}&prefixes=${encodeURIComponent(sets)}`)\n const json = (await res.json()) as { icons?: string[] }\n if (my === seq) results.value = json.icons ?? []\n } catch {\n if (my === seq) error.value = 'Icon search is unavailable (offline?)'\n } finally {\n if (my === seq) loading.value = false\n }\n }, 250)\n watch([query, prefix], () => search())\n onMounted(() => search())\n const rows = computed(() => {\n const out: string[][] = []\n for (let i = 0; i < results.value.length; i += props.columns) out.push(results.value.slice(i, i + props.columns))\n return out\n })\n return { model, query, prefix, prefixItems, results, rows, loading, error }\n },\n})\n</script>\n\n<style>\n.sq-icon-picker { display: flex; flex-direction: column; gap: var(--sq-space-2); padding: var(--sq-space-3); width: 100%; max-width: 480px; }\n.sq-icon-picker__bar { display: flex; gap: var(--sq-space-2); }\n.sq-icon-picker__prefix { max-width: 140px; }\n.sq-icon-picker__state { display: flex; align-items: center; justify-content: center; min-height: 120px; color: var(--sq-muted); font-size: var(--sq-text-sm); }\n.sq-icon-picker__row { display: flex; gap: 4px; height: 56px; padding: 4px 0; }\n.sq-icon-picker__cell { --sq-r: var(--sq-radius-md); flex: 1; display: flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--sq-on-surface); cursor: pointer; max-width: 56px; }\n.sq-icon-picker__cell:hover { background: color-mix(in srgb, var(--sq-primary) 10%, transparent); color: var(--sq-primary); }\n.sq-icon-picker__cell--active { background: var(--sq-primary) !important; color: var(--sq-on-primary) !important; }\n.sq-icon-picker__selected { display: flex; align-items: center; gap: var(--sq-space-2); padding-top: var(--sq-space-2); border-top: 1px solid var(--sq-border); font-size: var(--sq-text-sm); }\n</style>\n","import { addCollection, addIcons, applyOptions, resolveIcon } from '@squirkle-ui/ui'\nimport SqIconPicker from './SqIconPicker.vue'\n\nexport { SqIconPicker, addCollection, addIcons }\nexport const componentNames = ['SqIconPicker'] as const\n\n/** `$alias` presets mapping friendly names to popular Iconify sets. */\nexport const aliasPresets = {\n mdi: { home: 'mdi:home-outline', user: 'mdi:account-outline', settings: 'mdi:cog-outline', bell: 'mdi:bell-outline', heart: 'mdi:heart-outline', cart: 'mdi:cart-outline', chart: 'mdi:chart-box-outline', mail: 'mdi:email-outline', github: 'mdi:github' },\n lucide: { home: 'lucide:home', user: 'lucide:user', settings: 'lucide:settings', bell: 'lucide:bell', heart: 'lucide:heart', cart: 'lucide:shopping-cart', chart: 'lucide:bar-chart-3', mail: 'lucide:mail', github: 'lucide:github' },\n} as const\n\n/** Registers a preset so `<SqIcon icon=\"$home\">` resolves to that set. */\nexport function useIconPreset(name: keyof typeof aliasPresets) {\n applyOptions({ icons: { aliases: { ...aliasPresets[name] } } })\n}\n\n/** Warms the icon cache (batched Iconify API request) before icons render. */\nexport function preloadIcons(names: string[]) {\n names.forEach((n) => resolveIcon(n))\n}\n"],"names":["defineComponent","SqIcon","SqProgressCircular","SqSelect","SqShape","SqTextField","SqVirtualScroll","vModel","useProxiedModel","ref","computed","debounce","config","watch","onMounted","applyOptions","resolveIcon"],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { vModel, SqVirtualScroll, SqTextField, SqShape, SqSelect, SqProgressCircular, SqIcon, useProxiedModel, config, resolveIcon, applyOptions } from "@squirkle-ui/ui/vue2";
|
|
2
|
+
import { addCollection, addIcons } from "@squirkle-ui/ui/vue2";
|
|
3
|
+
import { defineComponent, ref, computed, watch, onMounted } from "vue";
|
|
4
|
+
import { debounce } from "@squirkle-ui/core";
|
|
5
|
+
const _sfc_main = defineComponent({
|
|
6
|
+
name: "SqIconPicker",
|
|
7
|
+
components: { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll },
|
|
8
|
+
model: vModel,
|
|
9
|
+
props: {
|
|
10
|
+
modelValue: { type: String, default: void 0 },
|
|
11
|
+
/** Limit to icon sets, e.g. ['mdi', 'lucide', 'tabler']. */
|
|
12
|
+
prefixes: { type: Array, default: () => ["mdi", "lucide", "tabler", "ph", "solar"] },
|
|
13
|
+
columns: { type: Number, default: 8 },
|
|
14
|
+
height: { type: [Number, String], default: 280 },
|
|
15
|
+
limit: { type: Number, default: 400 },
|
|
16
|
+
placeholder: { type: String, default: "Search icons…" }
|
|
17
|
+
},
|
|
18
|
+
emits: ["update:modelValue"],
|
|
19
|
+
setup(props, { emit }) {
|
|
20
|
+
const model = useProxiedModel(props, emit);
|
|
21
|
+
const query = ref("home");
|
|
22
|
+
const prefix = ref("all");
|
|
23
|
+
const results = ref([]);
|
|
24
|
+
const loading = ref(false);
|
|
25
|
+
const error = ref("");
|
|
26
|
+
const prefixItems = computed(() => [{ title: "All sets", value: "all" }, ...props.prefixes.map((p) => ({ title: p, value: p }))]);
|
|
27
|
+
let seq = 0;
|
|
28
|
+
const search = debounce(async () => {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
const q = ((_a = query.value) != null ? _a : "").trim();
|
|
31
|
+
const api = config.icons.api;
|
|
32
|
+
if (!q || !api) return results.value = [];
|
|
33
|
+
const my = ++seq;
|
|
34
|
+
loading.value = true;
|
|
35
|
+
error.value = "";
|
|
36
|
+
try {
|
|
37
|
+
const sets = prefix.value === "all" ? props.prefixes.join(",") : prefix.value;
|
|
38
|
+
const res = await fetch(`${api}/search?query=${encodeURIComponent(q)}&limit=${props.limit}&prefixes=${encodeURIComponent(sets)}`);
|
|
39
|
+
const json = await res.json();
|
|
40
|
+
if (my === seq) results.value = (_b = json.icons) != null ? _b : [];
|
|
41
|
+
} catch {
|
|
42
|
+
if (my === seq) error.value = "Icon search is unavailable (offline?)";
|
|
43
|
+
} finally {
|
|
44
|
+
if (my === seq) loading.value = false;
|
|
45
|
+
}
|
|
46
|
+
}, 250);
|
|
47
|
+
watch([query, prefix], () => search());
|
|
48
|
+
onMounted(() => search());
|
|
49
|
+
const rows = computed(() => {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (let i = 0; i < results.value.length; i += props.columns) out.push(results.value.slice(i, i + props.columns));
|
|
52
|
+
return out;
|
|
53
|
+
});
|
|
54
|
+
return { model, query, prefix, prefixItems, results, rows, loading, error };
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
function normalizeComponent(scriptExports, render2, staticRenderFns, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) {
|
|
58
|
+
var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports;
|
|
59
|
+
if (render2) {
|
|
60
|
+
options.render = render2;
|
|
61
|
+
options.staticRenderFns = staticRenderFns;
|
|
62
|
+
options._compiled = true;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
exports: scriptExports,
|
|
66
|
+
options
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
var _sfc_render = function render() {
|
|
70
|
+
var _vm = this, _c = _vm._self._c;
|
|
71
|
+
_vm._self._setupProxy;
|
|
72
|
+
return _c("div", { staticClass: "sq-icon-picker sq-surface", style: { "--sq-r": "var(--sq-radius-xl)", "--sq-bg": "var(--sq-surface-raised)", "--sq-bd": "var(--sq-border)", "--sq-bw": "1px" } }, [_c("SqShape"), _c("div", { staticClass: "sq-icon-picker__bar" }, [_c("SqTextField", { attrs: { "placeholder": _vm.placeholder, "prepend-inner-icon": "search", "density": "compact", "hide-details": "", "clearable": "" }, model: { value: _vm.query, callback: function($$v) {
|
|
73
|
+
_vm.query = $$v;
|
|
74
|
+
}, expression: "query" } }), _vm.prefixes.length > 1 ? _c("SqSelect", { staticClass: "sq-icon-picker__prefix", attrs: { "items": _vm.prefixItems, "density": "compact", "hide-details": "" }, model: { value: _vm.prefix, callback: function($$v) {
|
|
75
|
+
_vm.prefix = $$v;
|
|
76
|
+
}, expression: "prefix" } }) : _vm._e()], 1), _vm.loading ? _c("div", { staticClass: "sq-icon-picker__state" }, [_c("SqProgressCircular", { attrs: { "indeterminate": "", "size": 22, "width": 3 } })], 1) : _vm.error ? _c("div", { staticClass: "sq-icon-picker__state" }, [_vm._v(_vm._s(_vm.error))]) : !_vm.results.length ? _c("div", { staticClass: "sq-icon-picker__state" }, [_vm._v(_vm._s(_vm.query ? "No icons found" : "Type to search 200,000+ Iconify icons"))]) : _c("SqVirtualScroll", { attrs: { "items": _vm.rows, "item-height": 56, "height": _vm.height }, scopedSlots: _vm._u([{ key: "default", fn: function({ item }) {
|
|
77
|
+
return [_c("div", { staticClass: "sq-icon-picker__row" }, _vm._l(item, function(name) {
|
|
78
|
+
return _c("button", { key: name, staticClass: "sq-icon-picker__cell sq-squircle", class: { "sq-icon-picker__cell--active": name === _vm.model }, attrs: { "type": "button", "title": name, "aria-label": name, "aria-pressed": name === _vm.model ? "true" : "false" }, on: { "click": function($event) {
|
|
79
|
+
_vm.model = name;
|
|
80
|
+
} } }, [_c("SqIcon", { attrs: { "icon": name, "size": "24px" } })], 1);
|
|
81
|
+
}), 0)];
|
|
82
|
+
} }]) }), _vm.model ? _c("div", { staticClass: "sq-icon-picker__selected" }, [_c("SqIcon", { attrs: { "icon": _vm.model, "size": "20px" } }), _c("code", [_vm._v(_vm._s(_vm.model))])], 1) : _vm._e()], 1);
|
|
83
|
+
};
|
|
84
|
+
var _sfc_staticRenderFns = [];
|
|
85
|
+
var __component__ = /* @__PURE__ */ normalizeComponent(
|
|
86
|
+
_sfc_main,
|
|
87
|
+
_sfc_render,
|
|
88
|
+
_sfc_staticRenderFns
|
|
89
|
+
);
|
|
90
|
+
const SqIconPicker = __component__.exports;
|
|
91
|
+
const componentNames = ["SqIconPicker"];
|
|
92
|
+
const aliasPresets = {
|
|
93
|
+
mdi: { home: "mdi:home-outline", user: "mdi:account-outline", settings: "mdi:cog-outline", bell: "mdi:bell-outline", heart: "mdi:heart-outline", cart: "mdi:cart-outline", chart: "mdi:chart-box-outline", mail: "mdi:email-outline", github: "mdi:github" },
|
|
94
|
+
lucide: { home: "lucide:home", user: "lucide:user", settings: "lucide:settings", bell: "lucide:bell", heart: "lucide:heart", cart: "lucide:shopping-cart", chart: "lucide:bar-chart-3", mail: "lucide:mail", github: "lucide:github" }
|
|
95
|
+
};
|
|
96
|
+
function useIconPreset(name) {
|
|
97
|
+
applyOptions({ icons: { aliases: { ...aliasPresets[name] } } });
|
|
98
|
+
}
|
|
99
|
+
function preloadIcons(names) {
|
|
100
|
+
names.forEach((n) => resolveIcon(n));
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
SqIconPicker,
|
|
104
|
+
addCollection,
|
|
105
|
+
addIcons,
|
|
106
|
+
aliasPresets,
|
|
107
|
+
componentNames,
|
|
108
|
+
preloadIcons,
|
|
109
|
+
useIconPreset
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/SqIconPicker.vue","../../src/index.ts"],"sourcesContent":["<template>\n <div class=\"sq-icon-picker sq-surface\" :style=\"{ '--sq-r': 'var(--sq-radius-xl)', '--sq-bg': 'var(--sq-surface-raised)', '--sq-bd': 'var(--sq-border)', '--sq-bw': '1px' }\">\n <SqShape />\n <div class=\"sq-icon-picker__bar\">\n <SqTextField v-model=\"query\" :placeholder=\"placeholder\" prepend-inner-icon=\"search\" density=\"compact\" hide-details clearable />\n <SqSelect v-if=\"prefixes.length > 1\" v-model=\"prefix\" :items=\"prefixItems\" density=\"compact\" hide-details class=\"sq-icon-picker__prefix\" />\n </div>\n <div v-if=\"loading\" class=\"sq-icon-picker__state\"><SqProgressCircular indeterminate :size=\"22\" :width=\"3\" /></div>\n <div v-else-if=\"error\" class=\"sq-icon-picker__state\">{{ error }}</div>\n <div v-else-if=\"!results.length\" class=\"sq-icon-picker__state\">{{ query ? 'No icons found' : 'Type to search 200,000+ Iconify icons' }}</div>\n <SqVirtualScroll v-else :items=\"rows\" :item-height=\"56\" :height=\"height\">\n <template #default=\"{ item }\">\n <div class=\"sq-icon-picker__row\">\n <button\n v-for=\"name in item\"\n :key=\"name\"\n type=\"button\"\n class=\"sq-icon-picker__cell sq-squircle\"\n :class=\"{ 'sq-icon-picker__cell--active': name === model }\"\n :title=\"name\"\n :aria-label=\"name\"\n :aria-pressed=\"name === model ? 'true' : 'false'\"\n @click=\"model = name\"\n ><SqIcon :icon=\"name\" size=\"24px\" /></button>\n </div>\n </template>\n </SqVirtualScroll>\n <div v-if=\"model\" class=\"sq-icon-picker__selected\"><SqIcon :icon=\"model\" size=\"20px\" /><code>{{ model }}</code></div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, onMounted, ref, watch, type PropType } from 'vue'\nimport { debounce } from '@squirkle-ui/core'\nimport { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll, config, useProxiedModel, vModel } from '@squirkle-ui/ui'\n\n/** Searchable Iconify browser (uses the configured Iconify API host). v-model = icon name, e.g. `mdi:home`. */\nexport default defineComponent({\n name: 'SqIconPicker',\n components: { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll },\n model: vModel,\n props: {\n modelValue: { type: String, default: undefined },\n /** Limit to icon sets, e.g. ['mdi', 'lucide', 'tabler']. */\n prefixes: { type: Array as PropType<string[]>, default: () => ['mdi', 'lucide', 'tabler', 'ph', 'solar'] },\n columns: { type: Number, default: 8 },\n height: { type: [Number, String], default: 280 },\n limit: { type: Number, default: 400 },\n placeholder: { type: String, default: 'Search icons…' },\n },\n emits: ['update:modelValue'],\n setup(props, { emit }) {\n const model = useProxiedModel<string | undefined>(props, emit)\n const query = ref('home')\n const prefix = ref('all')\n const results = ref<string[]>([])\n const loading = ref(false)\n const error = ref('')\n const prefixItems = computed(() => [{ title: 'All sets', value: 'all' }, ...props.prefixes.map((p) => ({ title: p, value: p }))])\n let seq = 0\n const search = debounce(async () => {\n const q = (query.value ?? '').trim()\n const api = config.icons.api\n if (!q || !api) return (results.value = [])\n const my = ++seq\n loading.value = true\n error.value = ''\n try {\n const sets = prefix.value === 'all' ? props.prefixes.join(',') : prefix.value\n const res = await fetch(`${api}/search?query=${encodeURIComponent(q)}&limit=${props.limit}&prefixes=${encodeURIComponent(sets)}`)\n const json = (await res.json()) as { icons?: string[] }\n if (my === seq) results.value = json.icons ?? []\n } catch {\n if (my === seq) error.value = 'Icon search is unavailable (offline?)'\n } finally {\n if (my === seq) loading.value = false\n }\n }, 250)\n watch([query, prefix], () => search())\n onMounted(() => search())\n const rows = computed(() => {\n const out: string[][] = []\n for (let i = 0; i < results.value.length; i += props.columns) out.push(results.value.slice(i, i + props.columns))\n return out\n })\n return { model, query, prefix, prefixItems, results, rows, loading, error }\n },\n})\n</script>\n\n<style>\n.sq-icon-picker { display: flex; flex-direction: column; gap: var(--sq-space-2); padding: var(--sq-space-3); width: 100%; max-width: 480px; }\n.sq-icon-picker__bar { display: flex; gap: var(--sq-space-2); }\n.sq-icon-picker__prefix { max-width: 140px; }\n.sq-icon-picker__state { display: flex; align-items: center; justify-content: center; min-height: 120px; color: var(--sq-muted); font-size: var(--sq-text-sm); }\n.sq-icon-picker__row { display: flex; gap: 4px; height: 56px; padding: 4px 0; }\n.sq-icon-picker__cell { --sq-r: var(--sq-radius-md); flex: 1; display: flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--sq-on-surface); cursor: pointer; max-width: 56px; }\n.sq-icon-picker__cell:hover { background: color-mix(in srgb, var(--sq-primary) 10%, transparent); color: var(--sq-primary); }\n.sq-icon-picker__cell--active { background: var(--sq-primary) !important; color: var(--sq-on-primary) !important; }\n.sq-icon-picker__selected { display: flex; align-items: center; gap: var(--sq-space-2); padding-top: var(--sq-space-2); border-top: 1px solid var(--sq-border); font-size: var(--sq-text-sm); }\n</style>\n","import { addCollection, addIcons, applyOptions, resolveIcon } from '@squirkle-ui/ui'\nimport SqIconPicker from './SqIconPicker.vue'\n\nexport { SqIconPicker, addCollection, addIcons }\nexport const componentNames = ['SqIconPicker'] as const\n\n/** `$alias` presets mapping friendly names to popular Iconify sets. */\nexport const aliasPresets = {\n mdi: { home: 'mdi:home-outline', user: 'mdi:account-outline', settings: 'mdi:cog-outline', bell: 'mdi:bell-outline', heart: 'mdi:heart-outline', cart: 'mdi:cart-outline', chart: 'mdi:chart-box-outline', mail: 'mdi:email-outline', github: 'mdi:github' },\n lucide: { home: 'lucide:home', user: 'lucide:user', settings: 'lucide:settings', bell: 'lucide:bell', heart: 'lucide:heart', cart: 'lucide:shopping-cart', chart: 'lucide:bar-chart-3', mail: 'lucide:mail', github: 'lucide:github' },\n} as const\n\n/** Registers a preset so `<SqIcon icon=\"$home\">` resolves to that set. */\nexport function useIconPreset(name: keyof typeof aliasPresets) {\n applyOptions({ icons: { aliases: { ...aliasPresets[name] } } })\n}\n\n/** Warms the icon cache (batched Iconify API request) before icons render. */\nexport function preloadIcons(names: string[]) {\n names.forEach((n) => resolveIcon(n))\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ui = require("@squirkle-ui/ui");
|
|
4
|
+
const vue = require("vue");
|
|
5
|
+
const core = require("@squirkle-ui/core");
|
|
6
|
+
const _sfc_main = vue.defineComponent({
|
|
7
|
+
name: "SqIconPicker",
|
|
8
|
+
components: { SqIcon: ui.SqIcon, SqProgressCircular: ui.SqProgressCircular, SqSelect: ui.SqSelect, SqShape: ui.SqShape, SqTextField: ui.SqTextField, SqVirtualScroll: ui.SqVirtualScroll },
|
|
9
|
+
model: ui.vModel,
|
|
10
|
+
props: {
|
|
11
|
+
modelValue: { type: String, default: void 0 },
|
|
12
|
+
/** Limit to icon sets, e.g. ['mdi', 'lucide', 'tabler']. */
|
|
13
|
+
prefixes: { type: Array, default: () => ["mdi", "lucide", "tabler", "ph", "solar"] },
|
|
14
|
+
columns: { type: Number, default: 8 },
|
|
15
|
+
height: { type: [Number, String], default: 280 },
|
|
16
|
+
limit: { type: Number, default: 400 },
|
|
17
|
+
placeholder: { type: String, default: "Search icons…" }
|
|
18
|
+
},
|
|
19
|
+
emits: ["update:modelValue"],
|
|
20
|
+
setup(props, { emit }) {
|
|
21
|
+
const model = ui.useProxiedModel(props, emit);
|
|
22
|
+
const query = vue.ref("home");
|
|
23
|
+
const prefix = vue.ref("all");
|
|
24
|
+
const results = vue.ref([]);
|
|
25
|
+
const loading = vue.ref(false);
|
|
26
|
+
const error = vue.ref("");
|
|
27
|
+
const prefixItems = vue.computed(() => [{ title: "All sets", value: "all" }, ...props.prefixes.map((p) => ({ title: p, value: p }))]);
|
|
28
|
+
let seq = 0;
|
|
29
|
+
const search = core.debounce(async () => {
|
|
30
|
+
const q = (query.value ?? "").trim();
|
|
31
|
+
const api = ui.config.icons.api;
|
|
32
|
+
if (!q || !api) return results.value = [];
|
|
33
|
+
const my = ++seq;
|
|
34
|
+
loading.value = true;
|
|
35
|
+
error.value = "";
|
|
36
|
+
try {
|
|
37
|
+
const sets = prefix.value === "all" ? props.prefixes.join(",") : prefix.value;
|
|
38
|
+
const res = await fetch(`${api}/search?query=${encodeURIComponent(q)}&limit=${props.limit}&prefixes=${encodeURIComponent(sets)}`);
|
|
39
|
+
const json = await res.json();
|
|
40
|
+
if (my === seq) results.value = json.icons ?? [];
|
|
41
|
+
} catch {
|
|
42
|
+
if (my === seq) error.value = "Icon search is unavailable (offline?)";
|
|
43
|
+
} finally {
|
|
44
|
+
if (my === seq) loading.value = false;
|
|
45
|
+
}
|
|
46
|
+
}, 250);
|
|
47
|
+
vue.watch([query, prefix], () => search());
|
|
48
|
+
vue.onMounted(() => search());
|
|
49
|
+
const rows = vue.computed(() => {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (let i = 0; i < results.value.length; i += props.columns) out.push(results.value.slice(i, i + props.columns));
|
|
52
|
+
return out;
|
|
53
|
+
});
|
|
54
|
+
return { model, query, prefix, prefixItems, results, rows, loading, error };
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const _export_sfc = (sfc, props) => {
|
|
58
|
+
const target = sfc.__vccOpts || sfc;
|
|
59
|
+
for (const [key, val] of props) {
|
|
60
|
+
target[key] = val;
|
|
61
|
+
}
|
|
62
|
+
return target;
|
|
63
|
+
};
|
|
64
|
+
const _hoisted_1 = {
|
|
65
|
+
class: "sq-icon-picker sq-surface",
|
|
66
|
+
style: { "--sq-r": "var(--sq-radius-xl)", "--sq-bg": "var(--sq-surface-raised)", "--sq-bd": "var(--sq-border)", "--sq-bw": "1px" }
|
|
67
|
+
};
|
|
68
|
+
const _hoisted_2 = { class: "sq-icon-picker__bar" };
|
|
69
|
+
const _hoisted_3 = {
|
|
70
|
+
key: 0,
|
|
71
|
+
class: "sq-icon-picker__state"
|
|
72
|
+
};
|
|
73
|
+
const _hoisted_4 = {
|
|
74
|
+
key: 1,
|
|
75
|
+
class: "sq-icon-picker__state"
|
|
76
|
+
};
|
|
77
|
+
const _hoisted_5 = {
|
|
78
|
+
key: 2,
|
|
79
|
+
class: "sq-icon-picker__state"
|
|
80
|
+
};
|
|
81
|
+
const _hoisted_6 = { class: "sq-icon-picker__row" };
|
|
82
|
+
const _hoisted_7 = ["title", "aria-label", "aria-pressed", "onClick"];
|
|
83
|
+
const _hoisted_8 = {
|
|
84
|
+
key: 4,
|
|
85
|
+
class: "sq-icon-picker__selected"
|
|
86
|
+
};
|
|
87
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
88
|
+
const _component_SqShape = vue.resolveComponent("SqShape");
|
|
89
|
+
const _component_SqTextField = vue.resolveComponent("SqTextField");
|
|
90
|
+
const _component_SqSelect = vue.resolveComponent("SqSelect");
|
|
91
|
+
const _component_SqProgressCircular = vue.resolveComponent("SqProgressCircular");
|
|
92
|
+
const _component_SqIcon = vue.resolveComponent("SqIcon");
|
|
93
|
+
const _component_SqVirtualScroll = vue.resolveComponent("SqVirtualScroll");
|
|
94
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
|
|
95
|
+
vue.createVNode(_component_SqShape),
|
|
96
|
+
vue.createElementVNode("div", _hoisted_2, [
|
|
97
|
+
vue.createVNode(_component_SqTextField, {
|
|
98
|
+
modelValue: _ctx.query,
|
|
99
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.query = $event),
|
|
100
|
+
placeholder: _ctx.placeholder,
|
|
101
|
+
"prepend-inner-icon": "search",
|
|
102
|
+
density: "compact",
|
|
103
|
+
"hide-details": "",
|
|
104
|
+
clearable: ""
|
|
105
|
+
}, null, 8, ["modelValue", "placeholder"]),
|
|
106
|
+
_ctx.prefixes.length > 1 ? (vue.openBlock(), vue.createBlock(_component_SqSelect, {
|
|
107
|
+
key: 0,
|
|
108
|
+
modelValue: _ctx.prefix,
|
|
109
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => _ctx.prefix = $event),
|
|
110
|
+
items: _ctx.prefixItems,
|
|
111
|
+
density: "compact",
|
|
112
|
+
"hide-details": "",
|
|
113
|
+
class: "sq-icon-picker__prefix"
|
|
114
|
+
}, null, 8, ["modelValue", "items"])) : vue.createCommentVNode("v-if", true)
|
|
115
|
+
]),
|
|
116
|
+
_ctx.loading ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [
|
|
117
|
+
vue.createVNode(_component_SqProgressCircular, {
|
|
118
|
+
indeterminate: "",
|
|
119
|
+
size: 22,
|
|
120
|
+
width: 3
|
|
121
|
+
})
|
|
122
|
+
])) : _ctx.error ? (vue.openBlock(), vue.createElementBlock(
|
|
123
|
+
"div",
|
|
124
|
+
_hoisted_4,
|
|
125
|
+
vue.toDisplayString(_ctx.error),
|
|
126
|
+
1
|
|
127
|
+
/* TEXT */
|
|
128
|
+
)) : !_ctx.results.length ? (vue.openBlock(), vue.createElementBlock(
|
|
129
|
+
"div",
|
|
130
|
+
_hoisted_5,
|
|
131
|
+
vue.toDisplayString(_ctx.query ? "No icons found" : "Type to search 200,000+ Iconify icons"),
|
|
132
|
+
1
|
|
133
|
+
/* TEXT */
|
|
134
|
+
)) : (vue.openBlock(), vue.createBlock(_component_SqVirtualScroll, {
|
|
135
|
+
key: 3,
|
|
136
|
+
items: _ctx.rows,
|
|
137
|
+
"item-height": 56,
|
|
138
|
+
height: _ctx.height
|
|
139
|
+
}, {
|
|
140
|
+
default: vue.withCtx(({ item }) => [
|
|
141
|
+
vue.createElementVNode("div", _hoisted_6, [
|
|
142
|
+
(vue.openBlock(true), vue.createElementBlock(
|
|
143
|
+
vue.Fragment,
|
|
144
|
+
null,
|
|
145
|
+
vue.renderList(item, (name) => {
|
|
146
|
+
return vue.openBlock(), vue.createElementBlock("button", {
|
|
147
|
+
key: name,
|
|
148
|
+
type: "button",
|
|
149
|
+
class: vue.normalizeClass(["sq-icon-picker__cell sq-squircle", { "sq-icon-picker__cell--active": name === _ctx.model }]),
|
|
150
|
+
title: name,
|
|
151
|
+
"aria-label": name,
|
|
152
|
+
"aria-pressed": name === _ctx.model ? "true" : "false",
|
|
153
|
+
onClick: ($event) => _ctx.model = name
|
|
154
|
+
}, [
|
|
155
|
+
vue.createVNode(_component_SqIcon, {
|
|
156
|
+
icon: name,
|
|
157
|
+
size: "24px"
|
|
158
|
+
}, null, 8, ["icon"])
|
|
159
|
+
], 10, _hoisted_7);
|
|
160
|
+
}),
|
|
161
|
+
128
|
|
162
|
+
/* KEYED_FRAGMENT */
|
|
163
|
+
))
|
|
164
|
+
])
|
|
165
|
+
]),
|
|
166
|
+
_: 1
|
|
167
|
+
/* STABLE */
|
|
168
|
+
}, 8, ["items", "height"])),
|
|
169
|
+
_ctx.model ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [
|
|
170
|
+
vue.createVNode(_component_SqIcon, {
|
|
171
|
+
icon: _ctx.model,
|
|
172
|
+
size: "20px"
|
|
173
|
+
}, null, 8, ["icon"]),
|
|
174
|
+
vue.createElementVNode(
|
|
175
|
+
"code",
|
|
176
|
+
null,
|
|
177
|
+
vue.toDisplayString(_ctx.model),
|
|
178
|
+
1
|
|
179
|
+
/* TEXT */
|
|
180
|
+
)
|
|
181
|
+
])) : vue.createCommentVNode("v-if", true)
|
|
182
|
+
]);
|
|
183
|
+
}
|
|
184
|
+
const SqIconPicker = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
|
|
185
|
+
const componentNames = ["SqIconPicker"];
|
|
186
|
+
const aliasPresets = {
|
|
187
|
+
mdi: { home: "mdi:home-outline", user: "mdi:account-outline", settings: "mdi:cog-outline", bell: "mdi:bell-outline", heart: "mdi:heart-outline", cart: "mdi:cart-outline", chart: "mdi:chart-box-outline", mail: "mdi:email-outline", github: "mdi:github" },
|
|
188
|
+
lucide: { home: "lucide:home", user: "lucide:user", settings: "lucide:settings", bell: "lucide:bell", heart: "lucide:heart", cart: "lucide:shopping-cart", chart: "lucide:bar-chart-3", mail: "lucide:mail", github: "lucide:github" }
|
|
189
|
+
};
|
|
190
|
+
function useIconPreset(name) {
|
|
191
|
+
ui.applyOptions({ icons: { aliases: { ...aliasPresets[name] } } });
|
|
192
|
+
}
|
|
193
|
+
function preloadIcons(names) {
|
|
194
|
+
names.forEach((n) => ui.resolveIcon(n));
|
|
195
|
+
}
|
|
196
|
+
Object.defineProperty(exports, "addCollection", {
|
|
197
|
+
enumerable: true,
|
|
198
|
+
get: () => ui.addCollection
|
|
199
|
+
});
|
|
200
|
+
Object.defineProperty(exports, "addIcons", {
|
|
201
|
+
enumerable: true,
|
|
202
|
+
get: () => ui.addIcons
|
|
203
|
+
});
|
|
204
|
+
exports.SqIconPicker = SqIconPicker;
|
|
205
|
+
exports.aliasPresets = aliasPresets;
|
|
206
|
+
exports.componentNames = componentNames;
|
|
207
|
+
exports.preloadIcons = preloadIcons;
|
|
208
|
+
exports.useIconPreset = useIconPreset;
|
|
209
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/SqIconPicker.vue","../../src/index.ts"],"sourcesContent":["<template>\n <div class=\"sq-icon-picker sq-surface\" :style=\"{ '--sq-r': 'var(--sq-radius-xl)', '--sq-bg': 'var(--sq-surface-raised)', '--sq-bd': 'var(--sq-border)', '--sq-bw': '1px' }\">\n <SqShape />\n <div class=\"sq-icon-picker__bar\">\n <SqTextField v-model=\"query\" :placeholder=\"placeholder\" prepend-inner-icon=\"search\" density=\"compact\" hide-details clearable />\n <SqSelect v-if=\"prefixes.length > 1\" v-model=\"prefix\" :items=\"prefixItems\" density=\"compact\" hide-details class=\"sq-icon-picker__prefix\" />\n </div>\n <div v-if=\"loading\" class=\"sq-icon-picker__state\"><SqProgressCircular indeterminate :size=\"22\" :width=\"3\" /></div>\n <div v-else-if=\"error\" class=\"sq-icon-picker__state\">{{ error }}</div>\n <div v-else-if=\"!results.length\" class=\"sq-icon-picker__state\">{{ query ? 'No icons found' : 'Type to search 200,000+ Iconify icons' }}</div>\n <SqVirtualScroll v-else :items=\"rows\" :item-height=\"56\" :height=\"height\">\n <template #default=\"{ item }\">\n <div class=\"sq-icon-picker__row\">\n <button\n v-for=\"name in item\"\n :key=\"name\"\n type=\"button\"\n class=\"sq-icon-picker__cell sq-squircle\"\n :class=\"{ 'sq-icon-picker__cell--active': name === model }\"\n :title=\"name\"\n :aria-label=\"name\"\n :aria-pressed=\"name === model ? 'true' : 'false'\"\n @click=\"model = name\"\n ><SqIcon :icon=\"name\" size=\"24px\" /></button>\n </div>\n </template>\n </SqVirtualScroll>\n <div v-if=\"model\" class=\"sq-icon-picker__selected\"><SqIcon :icon=\"model\" size=\"20px\" /><code>{{ model }}</code></div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, onMounted, ref, watch, type PropType } from 'vue'\nimport { debounce } from '@squirkle-ui/core'\nimport { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll, config, useProxiedModel, vModel } from '@squirkle-ui/ui'\n\n/** Searchable Iconify browser (uses the configured Iconify API host). v-model = icon name, e.g. `mdi:home`. */\nexport default defineComponent({\n name: 'SqIconPicker',\n components: { SqIcon, SqProgressCircular, SqSelect, SqShape, SqTextField, SqVirtualScroll },\n model: vModel,\n props: {\n modelValue: { type: String, default: undefined },\n /** Limit to icon sets, e.g. ['mdi', 'lucide', 'tabler']. */\n prefixes: { type: Array as PropType<string[]>, default: () => ['mdi', 'lucide', 'tabler', 'ph', 'solar'] },\n columns: { type: Number, default: 8 },\n height: { type: [Number, String], default: 280 },\n limit: { type: Number, default: 400 },\n placeholder: { type: String, default: 'Search icons…' },\n },\n emits: ['update:modelValue'],\n setup(props, { emit }) {\n const model = useProxiedModel<string | undefined>(props, emit)\n const query = ref('home')\n const prefix = ref('all')\n const results = ref<string[]>([])\n const loading = ref(false)\n const error = ref('')\n const prefixItems = computed(() => [{ title: 'All sets', value: 'all' }, ...props.prefixes.map((p) => ({ title: p, value: p }))])\n let seq = 0\n const search = debounce(async () => {\n const q = (query.value ?? '').trim()\n const api = config.icons.api\n if (!q || !api) return (results.value = [])\n const my = ++seq\n loading.value = true\n error.value = ''\n try {\n const sets = prefix.value === 'all' ? props.prefixes.join(',') : prefix.value\n const res = await fetch(`${api}/search?query=${encodeURIComponent(q)}&limit=${props.limit}&prefixes=${encodeURIComponent(sets)}`)\n const json = (await res.json()) as { icons?: string[] }\n if (my === seq) results.value = json.icons ?? []\n } catch {\n if (my === seq) error.value = 'Icon search is unavailable (offline?)'\n } finally {\n if (my === seq) loading.value = false\n }\n }, 250)\n watch([query, prefix], () => search())\n onMounted(() => search())\n const rows = computed(() => {\n const out: string[][] = []\n for (let i = 0; i < results.value.length; i += props.columns) out.push(results.value.slice(i, i + props.columns))\n return out\n })\n return { model, query, prefix, prefixItems, results, rows, loading, error }\n },\n})\n</script>\n\n<style>\n.sq-icon-picker { display: flex; flex-direction: column; gap: var(--sq-space-2); padding: var(--sq-space-3); width: 100%; max-width: 480px; }\n.sq-icon-picker__bar { display: flex; gap: var(--sq-space-2); }\n.sq-icon-picker__prefix { max-width: 140px; }\n.sq-icon-picker__state { display: flex; align-items: center; justify-content: center; min-height: 120px; color: var(--sq-muted); font-size: var(--sq-text-sm); }\n.sq-icon-picker__row { display: flex; gap: 4px; height: 56px; padding: 4px 0; }\n.sq-icon-picker__cell { --sq-r: var(--sq-radius-md); flex: 1; display: flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--sq-on-surface); cursor: pointer; max-width: 56px; }\n.sq-icon-picker__cell:hover { background: color-mix(in srgb, var(--sq-primary) 10%, transparent); color: var(--sq-primary); }\n.sq-icon-picker__cell--active { background: var(--sq-primary) !important; color: var(--sq-on-primary) !important; }\n.sq-icon-picker__selected { display: flex; align-items: center; gap: var(--sq-space-2); padding-top: var(--sq-space-2); border-top: 1px solid var(--sq-border); font-size: var(--sq-text-sm); }\n</style>\n","import { addCollection, addIcons, applyOptions, resolveIcon } from '@squirkle-ui/ui'\nimport SqIconPicker from './SqIconPicker.vue'\n\nexport { SqIconPicker, addCollection, addIcons }\nexport const componentNames = ['SqIconPicker'] as const\n\n/** `$alias` presets mapping friendly names to popular Iconify sets. */\nexport const aliasPresets = {\n mdi: { home: 'mdi:home-outline', user: 'mdi:account-outline', settings: 'mdi:cog-outline', bell: 'mdi:bell-outline', heart: 'mdi:heart-outline', cart: 'mdi:cart-outline', chart: 'mdi:chart-box-outline', mail: 'mdi:email-outline', github: 'mdi:github' },\n lucide: { home: 'lucide:home', user: 'lucide:user', settings: 'lucide:settings', bell: 'lucide:bell', heart: 'lucide:heart', cart: 'lucide:shopping-cart', chart: 'lucide:bar-chart-3', mail: 'lucide:mail', github: 'lucide:github' },\n} as const\n\n/** Registers a preset so `<SqIcon icon=\"$home\">` resolves to that set. */\nexport function useIconPreset(name: keyof typeof aliasPresets) {\n applyOptions({ icons: { aliases: { ...aliasPresets[name] } } })\n}\n\n/** Warms the icon cache (batched Iconify API request) before icons render. */\nexport function preloadIcons(names: string[]) {\n names.forEach((n) => resolveIcon(n))\n}\n"],"names":["defineComponent","SqIcon","SqProgressCircular","SqSelect","SqShape","SqTextField","SqVirtualScroll","vModel","useProxiedModel","ref","computed","debounce","config","watch","onMounted","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_createBlock","_createCommentVNode","_toDisplayString","_withCtx","_Fragment","_renderList","_normalizeClass","applyOptions","resolveIcon"],"mappings":"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"}
|