refactor(webapp): migrate icons (#9238)
@ -81,10 +81,10 @@ Phase 0: ██████████ 100% (6/6 Aufgaben) ✅
|
||||
Phase 1: ██████████ 100% (6/6 Aufgaben) ✅
|
||||
Phase 2: ██████████ 100% (26/26 Aufgaben) ✅
|
||||
Phase 3: ██████████ 100% (24/24 Aufgaben) ✅ - Webapp-Integration komplett
|
||||
Phase 4: ██░░░░░░░░ 18% (3/17 Aufgaben) - OsButton ✅, OsIcon ✅, System-Icons ✅
|
||||
Phase 4: ██░░░░░░░░ 24% (4/17 Aufgaben) - OsButton ✅, OsIcon ✅, System-Icons ✅, BaseIcon→OsIcon Migration ✅
|
||||
Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben)
|
||||
───────────────────────────────────────
|
||||
Gesamt: ████████░░ 76% (65/86 Aufgaben)
|
||||
Gesamt: ████████░░ 77% (66/86 Aufgaben)
|
||||
```
|
||||
|
||||
### Katalogisierung (Details in KATALOG.md)
|
||||
@ -131,31 +131,53 @@ System-Icons:
|
||||
└─ plus.svg (Plus/Add)
|
||||
|
||||
Ocelot-Icons (separates Entry-Point):
|
||||
└─ angle-down.svg (Dropdown-Pfeil)
|
||||
└─ 82 Icons (Feature-Icons + Kategorie-Icons aus Webapp migriert)
|
||||
|
||||
BaseIcon → OsIcon Webapp-Migration: ✅
|
||||
├─ 131 <base-icon> in 70+ Dateien → <os-icon :icon="...">
|
||||
├─ 82 SVGs in ocelot/icons/svgs/ (inkl. 17 Kategorie-Icons)
|
||||
├─ vite-svg-icon Plugin erweitert (rect, circle, polygon, polyline, ellipse, line)
|
||||
├─ Kategorie-Icons: DB-String → toCamelCase() → ocelotIcons Lookup
|
||||
├─ Jest Mocks: @ocelot-social/ui/ocelot für ocelotIcons in Tests
|
||||
├─ Tests aktualisiert: 911/939 Tests bestanden (3 pre-existing failures)
|
||||
└─ 0 base-icon/BaseIcon Referenzen verbleibend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
**Letzte Aktualisierung:** 2026-02-15 (Session 21)
|
||||
**Letzte Aktualisierung:** 2026-02-15 (Session 22)
|
||||
|
||||
**Aktuelle Phase:** Phase 4 - OsIcon ✅ implementiert, System-Icons eingerichtet
|
||||
**Aktuelle Phase:** Phase 4 - OsIcon ✅, BaseIcon → OsIcon Webapp-Migration ✅
|
||||
|
||||
**Zuletzt abgeschlossen (Session 21 - OsIcon Komponente, System-Icons, Ocelot-Umbenennung):**
|
||||
**Zuletzt abgeschlossen (Session 22 - BaseIcon → OsIcon Webapp-Migration):**
|
||||
- [x] 131 `<base-icon>` Nutzungen in 70+ Dateien → `<os-icon :icon="icons.xxx">` migriert
|
||||
- [x] 82 Ocelot-Icons in `packages/ui/src/ocelot/icons/svgs/` (von 1 auf 82)
|
||||
- [x] 17 Kategorie-Icons aus Webapp kopiert (networking, energy, psyche, movement, finance, child, mobility, shopping-cart, peace, politics, nature, science, health, media, spirituality, culture, miscellaneous)
|
||||
- [x] vite-svg-icon Plugin erweitert: unterstützt `<rect>`, `<circle>`, `<polygon>`, `<polyline>`, `<ellipse>`, `<line>` (war path-only)
|
||||
- [x] Alle neuen SVGs auf Single-Line minifiziert (Multiline brach JS-String-Literale)
|
||||
- [x] Kategorie-Icons: DB-String → `toCamelCase()` → `ocelotIcons[key]` Lookup (Category/index.vue, CategoriesFilter.vue, CategoriesSelect.vue, admin/categories.vue)
|
||||
- [x] `created() { this.icons = ocelotIcons }` Pattern in allen Komponenten (non-reactive)
|
||||
- [x] MenuLegend.vue: `legendItems` von `data()` → `computed` (data() läuft vor created(), this.icons undefined)
|
||||
- [x] HeaderMenu.vue: Map-Button Icon-Größe korrigiert (`size="xl"` + negative Margin)
|
||||
- [x] ShowPassword.vue: `:data-test="iconName"` entfernt (Icon ist jetzt Render-Function, kein String)
|
||||
- [x] Jest-Tests aktualisiert: OsIcon + ocelotIcons statt BaseIcon + String-Namen
|
||||
- Category/index.spec.js, ProfileAvatar.spec.js, CounterIcon.spec.js, ReportRow.spec.js
|
||||
- ActionButton.spec.js, ComponentSlider.spec.js, ShowPassword.spec.js, LoginForm.spec.js
|
||||
- [x] 8 stale Snapshot-Dateien gelöscht
|
||||
- [x] Jest Mock: `test/__mocks__/@ocelot-social/ui/ocelot.js` für ocelotIcons in Tests
|
||||
- [x] CSS: `.base-icon` → `.os-icon` in main.scss und Category/index.vue
|
||||
- [x] 0 `base-icon`/`BaseIcon` Referenzen verbleibend in gesamter Webapp
|
||||
- [x] 911/939 Tests bestanden (3 pre-existing Jest worker crashes)
|
||||
|
||||
**Zuvor abgeschlossen (Session 21 - OsIcon Komponente, System-Icons, Ocelot-Umbenennung):**
|
||||
- [x] OsIcon Komponente implementiert (name, icon, size Props; Vue 2/3 via vue-demi h())
|
||||
- [x] System-Icons: check, close, plus (SVG, viewBox 0 0 32 32, stroke-basiert)
|
||||
- [x] Custom vite-svg-icon Plugin: SVG → Vue Render-Function via `?icon` Query
|
||||
- [x] Icon-Größen: xs(0.75em), sm(0.875em), md(1.2em), lg(1.5em), xl(2em), 2xl(2.5em)
|
||||
- [x] A11y: decorative (aria-hidden, default) / semantic (role="img" + aria-label)
|
||||
- [x] fill-current für Farbvererbung vom Parent
|
||||
- [x] OsButton nutzt OsIcon statt inline SVG für Icon-Rendering
|
||||
- [x] Ocelot-Icons: separates Entry-Point (ocelot.mjs) mit dynamischem Loading via import.meta.glob
|
||||
- [x] `src/webapp/` → `src/ocelot/` umbenannt (konsistentes Naming)
|
||||
- [x] check-completeness erweitert: unterstützt ocelot/ Verzeichnis
|
||||
- [x] OsIcon: 211 Zeilen Unit-Tests, Visual Tests mit checkA11y(), Keyboard A11y
|
||||
- [x] 100% Test-Coverage für OsIcon
|
||||
- [x] OsButton Stories bereinigt (OsIcon statt Inline-SVGs)
|
||||
|
||||
**Zuvor abgeschlossen (Session 20 - `as`-Prop + nuxt-link Migration):**
|
||||
- [x] OsButton: `as` Prop implementiert (polymorphe Komponente: `button`, `a`, `nuxt-link`, `router-link`, Custom-Komponenten)
|
||||
@ -200,8 +222,7 @@ Ocelot-Icons (separates Entry-Point):
|
||||
- [ ] OsSpinner Komponente (vereint DsSpinner + LoadingSpinner)
|
||||
- [ ] OsCard Komponente (vereint DsCard + BaseCard)
|
||||
- [ ] Weitere Tier 1 Komponenten
|
||||
- [ ] BaseIcon → OsIcon Webapp-Migration (131 Nutzungen)
|
||||
- [ ] Snapshots/Tests aktualisieren
|
||||
- [ ] Browser-Fehler untersuchen: `TypeError: Cannot read properties of undefined (reading 'heartO')` (ocelotIcons undefined im Browser trotz korrekter Webpack-Aliase)
|
||||
|
||||
**Manuelle Setup-Aufgaben (außerhalb Code):**
|
||||
- [ ] `NPM_TOKEN` als GitHub Secret einrichten (für npm publish in ui-release.yml)
|
||||
@ -475,6 +496,7 @@ Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Activ
|
||||
|
||||
**Infrastruktur**
|
||||
- [x] System-Icons einrichten ✅ vite-svg-icon Plugin, 3 System-Icons, Ocelot-Icons Entry-Point
|
||||
- [x] BaseIcon → OsIcon Webapp-Migration ✅ 131 Nutzungen, 82 Ocelot-Icons, 0 BaseIcon verbleibend
|
||||
- [ ] CI docs-check Workflow (JSDoc-Coverage, README-Aktualität)
|
||||
|
||||
### Phase 5: Finalisierung
|
||||
@ -768,7 +790,7 @@ import { ocelotIcons } from '@ocelot-social/ui/ocelot'
|
||||
| Styleguide (_all) | 616 | Nicht übernehmen (FontAwesome 4 komplett) |
|
||||
| Webapp (svgs) | 238 | Feature-Icons, bleiben in Webapp |
|
||||
| **Library (system)** | **3** | ✅ check, close, plus |
|
||||
| **Ocelot-Icons** | **1** | ✅ angle-down (separates Entry-Point) |
|
||||
| **Ocelot-Icons** | **82** | ✅ Feature-Icons + Kategorie-Icons (separates Entry-Point) |
|
||||
|
||||
---
|
||||
|
||||
@ -1543,6 +1565,18 @@ Bei der Migration werden:
|
||||
| 2026-02-15 | **OsButton Stories** | Bereinigt: Inline-SVG-Komponenten durch OsIcon ersetzt; WithAriaLabel-Story entfernt; InheritColor-Story vereinfacht |
|
||||
| 2026-02-15 | **check-completeness** | Erweitert für ocelot/ Verzeichnis; unterstützt OsIcon-Patterns |
|
||||
| 2026-02-15 | **svg-icon.d.ts** | TypeScript-Deklaration für `?icon` Import-Query (Component-Typ) |
|
||||
| 2026-02-15 | **BaseIcon → OsIcon Migration** | 131 `<base-icon>` in 70+ Dateien → `<os-icon :icon="icons.xxx">`, 0 BaseIcon verbleibend |
|
||||
| 2026-02-15 | **82 Ocelot-Icons** | Von 1 auf 82 Icons: Feature-Icons + 17 Kategorie-Icons aus Webapp kopiert |
|
||||
| 2026-02-15 | **vite-svg-icon erweitert** | Unterstützt jetzt `<rect>`, `<circle>`, `<polygon>`, `<polyline>`, `<ellipse>`, `<line>` (war path-only) |
|
||||
| 2026-02-15 | **SVG-Minifizierung** | Alle 21 neuen SVGs auf Single-Line minifiziert (Multiline brach JS-String-Literale im Plugin) |
|
||||
| 2026-02-15 | **Kategorie-Icons** | DB-String → `toCamelCase()` → `ocelotIcons[key]` Lookup in Category, CategoriesFilter, CategoriesSelect, admin/categories |
|
||||
| 2026-02-15 | **MenuLegend.vue Fix** | `legendItems` von `data()` → `computed` (`data()` läuft vor `created()`, `this.icons` undefined) |
|
||||
| 2026-02-15 | **HeaderMenu.vue Fix** | Map-Button Icon-Größe: `size="xl"` + negative Margin (OsIcon md=1.2em vs BaseIcon --large=2.2em) |
|
||||
| 2026-02-15 | **ShowPassword.vue Fix** | `:data-test="iconName"` entfernt (Icon ist jetzt Render-Function statt String) |
|
||||
| 2026-02-15 | **Test-Updates (8 Specs)** | Category, ProfileAvatar, CounterIcon, ReportRow, ActionButton, ComponentSlider, ShowPassword, LoginForm: BaseIcon → OsIcon + ocelotIcons |
|
||||
| 2026-02-15 | **8 Snapshots gelöscht** | Stale Snapshot-Dateien entfernt nach BaseIcon → OsIcon Migration |
|
||||
| 2026-02-15 | **CSS Migration** | `.base-icon` → `.os-icon` in main.scss und Category/index.vue |
|
||||
| 2026-02-15 | **Jest Mock ocelot** | `test/__mocks__/@ocelot-social/ui/ocelot.js` für ocelotIcons in Jest-Umgebung |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -144,21 +144,23 @@
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "dist/index.mjs",
|
||||
"limit": "16 kB",
|
||||
"limit": "15 kB",
|
||||
"brotli": true
|
||||
},
|
||||
{
|
||||
"path": "dist/tailwind.preset.mjs",
|
||||
"limit": "2 kB"
|
||||
"limit": "1 kB",
|
||||
"brotli": true
|
||||
},
|
||||
{
|
||||
"path": "dist/ocelot.mjs",
|
||||
"limit": "5 kB",
|
||||
"limit": "45 kB",
|
||||
"brotli": true
|
||||
},
|
||||
{
|
||||
"path": "dist/style.css",
|
||||
"limit": "10 kB"
|
||||
"limit": "5 kB",
|
||||
"brotli": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { defineComponent, h } from 'vue-demi'
|
||||
import { defineComponent, h, markRaw } from 'vue-demi'
|
||||
|
||||
import OsButton from './OsButton.vue'
|
||||
|
||||
@ -570,16 +570,18 @@ describe('osButton', () => {
|
||||
})
|
||||
|
||||
it('renders a component passed as as', () => {
|
||||
const FakeLink = defineComponent({
|
||||
props: { to: { type: String, default: undefined } },
|
||||
setup(props, { slots }) {
|
||||
return () => h('a', { href: props.to }, slots.default?.())
|
||||
},
|
||||
})
|
||||
const FakeLink = markRaw(
|
||||
defineComponent({
|
||||
props: { to: { type: String, default: undefined } },
|
||||
setup(props, { slots }) {
|
||||
return () => h('a', { href: props.to }, slots.default?.())
|
||||
},
|
||||
}),
|
||||
)
|
||||
const wrapper = mount(OsButton, {
|
||||
props: { as: FakeLink },
|
||||
attrs: { to: '/groups' },
|
||||
slots: { default: 'Groups' },
|
||||
slots: { default: () => 'Groups' },
|
||||
})
|
||||
|
||||
expect((wrapper.element as HTMLElement).tagName).toBe('A')
|
||||
|
||||
@ -237,15 +237,15 @@
|
||||
/* v8 ignore stop */
|
||||
|
||||
const { class: attrClass, ...restAttrs } = attrs as Record<string, unknown>
|
||||
return h(
|
||||
tag,
|
||||
{
|
||||
...buttonData,
|
||||
class: cn(buttonClass, attrClass || ''),
|
||||
...restAttrs,
|
||||
},
|
||||
children,
|
||||
)
|
||||
const nodeProps = {
|
||||
...buttonData,
|
||||
class: cn(buttonClass, attrClass || ''),
|
||||
...restAttrs,
|
||||
}
|
||||
// Components expect slot functions; HTML elements accept VNode arrays
|
||||
return typeof tag === 'string'
|
||||
? h(tag, nodeProps, children)
|
||||
: h(tag, nodeProps, { default: () => children })
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@ -62,7 +62,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
||||
}),
|
||||
}
|
||||
|
||||
export const AllSystemIcons: Story = {
|
||||
export const AllIcons: Story = {
|
||||
render: () => ({
|
||||
components: { OsIcon },
|
||||
setup() {
|
||||
|
||||
@ -32,7 +32,7 @@ async function checkA11y(page: Page) {
|
||||
|
||||
test.describe('OsIcon keyboard accessibility', () => {
|
||||
test('decorative icons are not focusable', async ({ page }) => {
|
||||
await page.goto(`${STORY_URL}--all-system-icons&viewMode=story`)
|
||||
await page.goto(`${STORY_URL}--all-icons&viewMode=story`)
|
||||
const root = page.locator(STORY_ROOT)
|
||||
await root.waitFor()
|
||||
|
||||
@ -50,13 +50,13 @@ test.describe('OsIcon keyboard accessibility', () => {
|
||||
})
|
||||
|
||||
test.describe('OsIcon visual regression', () => {
|
||||
test('all system icons', async ({ page }) => {
|
||||
await page.goto(`${STORY_URL}--all-system-icons&viewMode=story`)
|
||||
test('all icons', async ({ page }) => {
|
||||
await page.goto(`${STORY_URL}--all-icons&viewMode=story`)
|
||||
const root = page.locator(STORY_ROOT)
|
||||
await root.waitFor()
|
||||
await waitForFonts(page)
|
||||
|
||||
await expect(root.locator('.grid')).toHaveScreenshot('all-system-icons.png')
|
||||
await expect(root.locator('.grid')).toHaveScreenshot('all-icons.png')
|
||||
|
||||
await checkA11y(page)
|
||||
})
|
||||
|
||||
@ -49,12 +49,19 @@
|
||||
const sizeClass = ICON_SIZES[props.size]
|
||||
|
||||
// Vue 2's h() cannot handle plain arrow functions as components (only
|
||||
// constructor functions or option objects). SYSTEM_ICONS entries are
|
||||
// arrow functions that return VNodes, so call them directly.
|
||||
// constructor functions or option objects). Icon render functions are
|
||||
// arrow functions that accept optional (h, isVue2) and return VNodes.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const isRenderFn = typeof iconComponent === 'function' && !(iconComponent as any).cid
|
||||
// In Vue 2, pass $createElement (bound to this instance) so icons avoid
|
||||
// the globally-imported h() which requires currentInstance in Vue 2.7.
|
||||
const createElement = /* v8 ignore next -- Vue 2 only */ isVue2
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(instance?.proxy as any)?.$createElement
|
||||
: h
|
||||
|
||||
const iconVNode = isRenderFn
|
||||
? (iconComponent as () => ReturnType<typeof h>)()
|
||||
? (iconComponent as (...args: unknown[]) => ReturnType<typeof h>)(createElement, isVue2)
|
||||
: h(iconComponent)
|
||||
|
||||
/* v8 ignore start -- Vue 2 branch tested in webapp Jest tests */
|
||||
@ -75,7 +82,7 @@
|
||||
{
|
||||
class: [
|
||||
cn(
|
||||
'os-icon inline-flex items-center shrink-0',
|
||||
'os-icon inline-flex items-center align-bottom shrink-0',
|
||||
sizeClass,
|
||||
'[&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current',
|
||||
),
|
||||
@ -103,7 +110,7 @@
|
||||
'span',
|
||||
{
|
||||
class: cn(
|
||||
'os-icon inline-flex items-center shrink-0',
|
||||
'os-icon inline-flex items-center align-bottom shrink-0',
|
||||
sizeClass,
|
||||
'[&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current',
|
||||
attrClass as ClassValue,
|
||||
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,4 +1,4 @@
|
||||
import { OsIcon, SYSTEM_ICONS } from '#src/components/OsIcon'
|
||||
import { OsIcon } from '#src/components/OsIcon'
|
||||
|
||||
import { ocelotIcons } from './index'
|
||||
|
||||
@ -11,33 +11,20 @@ const meta: Meta = {
|
||||
|
||||
export default meta
|
||||
|
||||
const systemEntries = Object.entries(SYSTEM_ICONS)
|
||||
const ocelotEntries = Object.entries(ocelotIcons)
|
||||
const allEntries = Object.entries(ocelotIcons)
|
||||
|
||||
export const AllIcons: StoryObj = {
|
||||
render: () => ({
|
||||
components: { OsIcon },
|
||||
setup() {
|
||||
return { systemEntries, ocelotEntries }
|
||||
return { allEntries }
|
||||
},
|
||||
template: `
|
||||
<div data-testid="icon-gallery" class="flex flex-col gap-6">
|
||||
<div>
|
||||
<h3 class="text-sm font-bold mb-2">Library Icons</h3>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div v-for="[name, icon] in systemEntries" :key="name" class="flex flex-col items-center gap-2 p-3 rounded border border-gray-200">
|
||||
<OsIcon :icon="icon" size="xl" />
|
||||
<span class="text-xs text-gray-600">{{ name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold mb-2">Ocelot Icons</h3>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div v-for="[name, icon] in ocelotEntries" :key="name" class="flex flex-col items-center gap-2 p-3 rounded border border-gray-200">
|
||||
<OsIcon :icon="icon" size="xl" />
|
||||
<span class="text-xs text-gray-600">{{ name }}</span>
|
||||
</div>
|
||||
<div data-testid="icon-gallery">
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div v-for="[name, icon] in allEntries" :key="name" class="flex flex-col items-center gap-2 p-3 rounded border border-gray-200">
|
||||
<OsIcon :icon="icon" size="xl" />
|
||||
<span class="text-xs text-gray-600">{{ name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 76 KiB |
@ -1,13 +1,12 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, expectTypeOf, it } from 'vitest'
|
||||
import { h } from 'vue'
|
||||
|
||||
import OsIcon from '#src/components/OsIcon/OsIcon.vue'
|
||||
|
||||
import { ocelotIcons } from './index'
|
||||
|
||||
describe('ocelot icons', () => {
|
||||
const IconAngleDown = ocelotIcons.IconAngleDown
|
||||
const angleDown = ocelotIcons.angleDown
|
||||
|
||||
describe('exports', () => {
|
||||
it('exports ocelotIcons as a record of functions', () => {
|
||||
@ -17,20 +16,21 @@ describe('ocelot icons', () => {
|
||||
})
|
||||
|
||||
it('auto-discovers all SVGs in svgs/ directory', () => {
|
||||
expect(ocelotIcons).toHaveProperty('IconAngleDown')
|
||||
expect(ocelotIcons).toHaveProperty('angleDown')
|
||||
expect(angleDown).toBeTypeOf('function')
|
||||
})
|
||||
|
||||
expectTypeOf(IconAngleDown).toBeFunction()
|
||||
it('includes system icons (check, close, plus)', () => {
|
||||
expect(ocelotIcons).toHaveProperty('check')
|
||||
expect(ocelotIcons).toHaveProperty('close')
|
||||
expect(ocelotIcons).toHaveProperty('plus')
|
||||
})
|
||||
})
|
||||
|
||||
describe('iconAngleDown', () => {
|
||||
describe('angleDown', () => {
|
||||
it('renders an SVG with correct viewBox', () => {
|
||||
const vnode = IconAngleDown()
|
||||
|
||||
expect(vnode).toBeDefined()
|
||||
|
||||
const wrapper = mount({
|
||||
render: () => h('div', [IconAngleDown()]),
|
||||
const wrapper = mount(OsIcon, {
|
||||
props: { icon: angleDown },
|
||||
})
|
||||
const svg = wrapper.find('svg')
|
||||
|
||||
@ -40,7 +40,7 @@ describe('ocelot icons', () => {
|
||||
|
||||
it('works with OsIcon :icon prop', () => {
|
||||
const wrapper = mount(OsIcon, {
|
||||
props: { icon: IconAngleDown },
|
||||
props: { icon: angleDown },
|
||||
})
|
||||
|
||||
expect(wrapper.find('.os-icon').exists()).toBe(true)
|
||||
@ -52,7 +52,7 @@ describe('ocelot icons', () => {
|
||||
describe('keyboard accessibility', () => {
|
||||
it('icon via :icon prop is not focusable (decorative element)', () => {
|
||||
const wrapper = mount(OsIcon, {
|
||||
props: { icon: IconAngleDown },
|
||||
props: { icon: angleDown },
|
||||
})
|
||||
|
||||
expect(wrapper.attributes('tabindex')).toBeUndefined()
|
||||
|
||||
@ -1,24 +1,19 @@
|
||||
import type { VNode } from 'vue-demi'
|
||||
import { SYSTEM_ICONS } from '#src/components/OsIcon/icons'
|
||||
|
||||
const modules = import.meta.glob<() => VNode>('./svgs/*.svg', {
|
||||
import type { Component } from 'vue-demi'
|
||||
|
||||
const modules = import.meta.glob<Component>('./svgs/*.svg', {
|
||||
query: '?icon',
|
||||
eager: true,
|
||||
import: 'default',
|
||||
})
|
||||
|
||||
function toName(path: string): string {
|
||||
return (
|
||||
'Icon' +
|
||||
path
|
||||
.replace('./svgs/', '')
|
||||
.replace('.svg', '')
|
||||
.split('-')
|
||||
.filter(Boolean)
|
||||
.map((s) => s[0].toUpperCase() + s.slice(1))
|
||||
.join('')
|
||||
)
|
||||
const parts = path.replace('./svgs/', '').replace('.svg', '').split('-').filter(Boolean)
|
||||
return parts.map((s, i) => (i === 0 ? s : s[0].toUpperCase() + s.slice(1))).join('')
|
||||
}
|
||||
|
||||
export const ocelotIcons: Record<string, () => VNode> = Object.fromEntries(
|
||||
Object.entries(modules).map(([path, icon]) => [toName(path), icon]),
|
||||
)
|
||||
export const ocelotIcons: Record<string, Component> = {
|
||||
...SYSTEM_ICONS,
|
||||
...Object.fromEntries(Object.entries(modules).map(([path, icon]) => [toName(path), icon])),
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 275 B |
|
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 294 B |
|
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 294 B |
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
|
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 820 B |
|
Before Width: | Height: | Size: 468 B After Width: | Height: | Size: 468 B |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
4
packages/ui/src/ocelot/icons/svgs/bell-slashed.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<path d="M15 3c1.105 0 2 0.895 2 2 0 0.085-0.021 0.168-0.031 0.25 3.521 0.924 6.031 4.273 6.031 8.031v8.719c0 0.565 0.435 1 1 1h1v2h-7.188c0.114 0.316 0.188 0.647 0.188 1 0 1.645-1.355 3-3 3s-3-1.355-3-3c0-0.353 0.073-0.684 0.188-1h-7.188v-2h1c0.565 0 1-0.435 1-1v-9c0-3.726 2.574-6.866 6.031-7.75-0.010-0.082-0.031-0.165-0.031-0.25 0-1.105 0.895-2 2-2zM14.563 7c-3.118 0.226-5.563 2.824-5.563 6v9c0 0.353-0.073 0.684-0.188 1h12.375c-0.114-0.316-0.188-0.647-0.188-1v-8.719c0-3.319-2.546-6.183-5.813-6.281-0.064-0.002-0.124-0-0.188 0-0.148 0-0.292-0.011-0.438 0zM15 25c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"/>
|
||||
<path d="M3.719 2.281l1.438-1.438 26 26-1.438 1.438z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 793 B After Width: | Height: | Size: 793 B |
|
Before Width: | Height: | Size: 531 B After Width: | Height: | Size: 531 B |
1
packages/ui/src/ocelot/icons/svgs/book.svg
Normal file
@ -0,0 +1 @@
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>book</title><path d="M10 5c2.92 0 5.482 0.981 6 1.188 0.518-0.206 3.080-1.188 6-1.188 3.227 0 6.375 1.313 6.375 1.313l0.625 0.281v20.406h-11.281c-0.346 0.597-0.979 1-1.719 1s-1.373-0.403-1.719-1h-11.281v-20.406l0.625-0.281s3.148-1.313 6.375-1.313zM10 7c-2.199 0-4.232 0.69-5 0.969v16.125c1.188-0.392 2.897-0.875 5-0.875 2.057 0 3.888 0.506 5 0.875v-16.125c-1-0.343-3.067-0.969-5-0.969zM22 7c-1.933 0-4 0.626-5 0.969v16.125c1.112-0.369 2.943-0.875 5-0.875 2.103 0 3.813 0.483 5 0.875v-16.125c-0.768-0.279-2.801-0.969-5-0.969z"></path></svg>
|
||||
|
After Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 309 B After Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 445 B After Width: | Height: | Size: 445 B |
|
Before Width: | Height: | Size: 961 B After Width: | Height: | Size: 961 B |
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 388 B |
1
packages/ui/src/ocelot/icons/svgs/comments.svg
Executable file
@ -0,0 +1 @@
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>comments</title><path d="M2 5h20v16h-9.656l-4.719 3.781-1.625 1.313v-5.094h-4v-16zM4 7v12h4v2.906l3.375-2.688 0.281-0.219h8.344v-12h-16zM24 9h6v16h-4v5.094l-6.344-5.094h-9.313l2.5-2h7.5l3.656 2.906v-2.906h4v-12h-4v-2z"></path></svg>
|
||||
|
After Width: | Height: | Size: 369 B |
|
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
|
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 302 B |
|
Before Width: | Height: | Size: 610 B After Width: | Height: | Size: 610 B |
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 374 B |
0
webapp/assets/_new/icons/svgs/exclamation-circle.svg → packages/ui/src/ocelot/icons/svgs/exclamation-circle.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 375 B |
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 355 B After Width: | Height: | Size: 355 B |
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 882 B |
1
packages/ui/src/ocelot/icons/svgs/hand-pointer.svg
Normal file
@ -0,0 +1 @@
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>hand-pointer-o</title><path d="M13 2c1.645 0 3 1.355 3 3v4.188c0.316-0.114 0.647-0.188 1-0.188 0.767 0 1.467 0.3 2 0.781 0.533-0.481 1.233-0.781 2-0.781 1.395 0 2.578 0.982 2.906 2.281 0.368-0.163 0.762-0.281 1.188-0.281 1.645 0 3 1.355 3 3v7.813c0 4.533-3.654 8.188-8.188 8.188h-1.719c-1.935 0-3.651-0.675-5-1.688l-0.031-0.063-0.063-0.031-8.188-8.094v-0.031c-1.154-1.154-1.154-3.034 0-4.188s3.034-1.154 4.188 0l0.25 0.219 0.656 0.688v-11.813c0-1.645 1.355-3 3-3zM13 4c-0.555 0-1 0.445-1 1v16.625l-4.313-4.313c-0.446-0.446-0.929-0.446-1.375 0s-0.446 0.929 0 1.375l8.094 8c1.051 0.788 2.317 1.313 3.781 1.313h1.719c3.467 0 6.188-2.721 6.188-6.188v-7.813c0-0.555-0.445-1-1-1s-1 0.445-1 1v2h-2.094v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-11c0-0.555-0.445-1-1-1z"></path></svg>
|
||||
|
After Width: | Height: | Size: 953 B |
1
packages/ui/src/ocelot/icons/svgs/heart-o.svg
Executable file
@ -0,0 +1 @@
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>heart-o</title><path d="M9.5 5c3.433 0 5.645 2.066 6.5 2.938 0.855-0.871 3.067-2.938 6.5-2.938 4.138 0 7.5 3.404 7.5 7.5 0 2.857-2.469 5.031-2.469 5.031l-11.531 11.563-0.719-0.719-10.813-10.844s-0.619-0.573-1.219-1.469-1.25-2.134-1.25-3.563c0-4.096 3.362-7.5 7.5-7.5zM9.5 7c-3.042 0-5.5 2.496-5.5 5.5 0 0.772 0.423 1.716 0.906 2.438s0.969 1.188 0.969 1.188l10.125 10.125 10.125-10.125s1.875-2.080 1.875-3.625c0-3.004-2.458-5.5-5.5-5.5-2.986 0-5.75 2.906-5.75 2.906l-0.75 0.844-0.75-0.844s-2.764-2.906-5.75-2.906z"></path></svg>
|
||||
|
After Width: | Height: | Size: 664 B |
|
Before Width: | Height: | Size: 334 B After Width: | Height: | Size: 334 B |
|
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 425 B |
|
Before Width: | Height: | Size: 468 B After Width: | Height: | Size: 468 B |
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 759 B After Width: | Height: | Size: 759 B |
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 330 B |
0
webapp/assets/_new/icons/svgs/map-marker.svg → packages/ui/src/ocelot/icons/svgs/map-marker.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 720 B After Width: | Height: | Size: 720 B |
|
Before Width: | Height: | Size: 634 B After Width: | Height: | Size: 634 B |
|
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 785 B After Width: | Height: | Size: 785 B |
|
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
|
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
|
Before Width: | Height: | Size: 726 B After Width: | Height: | Size: 726 B |
1
packages/ui/src/ocelot/icons/svgs/shopping-cart.svg
Executable file
@ -0,0 +1 @@
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>shopping-cart</title><path d="M4.75 7h2.219c0.918 0 1.716 0.61 1.938 1.5l2.625 10.5h11.469l1.906-7h-13.656l-0.5-2h16.75l-2.594 9.531c-0.238 0.87-1.004 1.469-1.906 1.469h-11.469c-0.917 0-1.714-0.61-1.938-1.5l-2.625-10.5h-2.219c-0.552 0-1-0.448-1-1s0.448-1 1-1zM21.75 21c1.645 0 3 1.355 3 3s-1.355 3-3 3-3-1.355-3-3 1.355-3 3-3zM12.75 21c1.645 0 3 1.355 3 3s-1.355 3-3 3-3-1.355-3-3 1.355-3 3-3zM12.75 23c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1zM21.75 23c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"></path></svg>
|
||||
|
After Width: | Height: | Size: 679 B |
0
webapp/assets/_new/icons/svgs/sign-in.svg → packages/ui/src/ocelot/icons/svgs/sign-in.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 405 B After Width: | Height: | Size: 405 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
0
webapp/assets/_new/icons/svgs/trash.svg → packages/ui/src/ocelot/icons/svgs/trash.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 282 B |
|
Before Width: | Height: | Size: 962 B After Width: | Height: | Size: 962 B |
0
webapp/assets/_new/icons/svgs/user-plus.svg → packages/ui/src/ocelot/icons/svgs/user-plus.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 698 B |
|
Before Width: | Height: | Size: 809 B After Width: | Height: | Size: 809 B |
|
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B |
0
webapp/assets/_new/icons/svgs/users.svg → packages/ui/src/ocelot/icons/svgs/users.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 724 B |
|
Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 483 B |
|
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 768 B |
0
webapp/assets/_new/icons/svgs/warning.svg → packages/ui/src/ocelot/icons/svgs/warning.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
@ -7,9 +7,28 @@ const SUFFIX = '?icon'
|
||||
|
||||
/** Escape a string for safe embedding in a single-quoted JS literal */
|
||||
function escapeJS(str: string): string {
|
||||
return str.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
|
||||
return str
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\u2028/g, '\\u2028')
|
||||
.replace(/\u2029/g, '\\u2029')
|
||||
}
|
||||
|
||||
const SUPPORTED_ELEMENTS = ['path', 'circle', 'rect', 'polygon', 'polyline', 'ellipse', 'line']
|
||||
const ELEM_PATTERN = SUPPORTED_ELEMENTS.join('|')
|
||||
// Built from constant array above — safe to use in RegExp
|
||||
// eslint-disable-next-line security/detect-non-literal-regexp
|
||||
const ELEM_REGEX = new RegExp(`<(${ELEM_PATTERN})(?:\\s([^>]*?))?\\/?>`, 'g')
|
||||
|
||||
// Elements silently ignored (container without visual effect)
|
||||
const IGNORED_ELEMENTS = ['g', 'title']
|
||||
const KNOWN_ELEMENTS = ['svg', ...SUPPORTED_ELEMENTS, ...IGNORED_ELEMENTS]
|
||||
// Built from constant arrays above — safe to use in RegExp
|
||||
// eslint-disable-next-line security/detect-non-literal-regexp
|
||||
const UNSUPPORTED_REGEX = new RegExp(`<(?!\\/|(?:${KNOWN_ELEMENTS.join('|')})\\b)(\\w+)[\\s>]`, 'g')
|
||||
|
||||
export default function svgIcon(): Plugin {
|
||||
return {
|
||||
name: 'svg-icon',
|
||||
@ -32,32 +51,43 @@ export default function svgIcon(): Plugin {
|
||||
const viewBoxMatch = viewBoxRegex.exec(svg)
|
||||
const viewBox = viewBoxMatch ? viewBoxMatch[1] : '0 0 32 32'
|
||||
|
||||
const unsupported = svg.match(/<(?:circle|rect|polygon|polyline|ellipse|line)\s/g)
|
||||
const unsupported = svg.match(UNSUPPORTED_REGEX)
|
||||
if (unsupported) {
|
||||
this.warn(
|
||||
`${filePath}: unsupported SVG elements will be ignored: ${[...new Set(unsupported.map((s) => s.trim()))].join(', ')}`,
|
||||
)
|
||||
}
|
||||
|
||||
const paths: string[] = []
|
||||
const pathRegex = /<path\s[^>]*?\bd="([^"]+)"/g
|
||||
const children: string[] = []
|
||||
ELEM_REGEX.lastIndex = 0
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pathRegex.exec(svg)) !== null) {
|
||||
paths.push(match[1])
|
||||
while ((match = ELEM_REGEX.exec(svg)) !== null) {
|
||||
const tag = match[1]
|
||||
const attrString = match[2] || ''
|
||||
const attrs: Record<string, string> = {}
|
||||
const attrRegex = /(\w[\w-]*)=(["'])([^"']*)\2/g
|
||||
let attrMatch: RegExpExecArray | null
|
||||
while ((attrMatch = attrRegex.exec(attrString)) !== null) {
|
||||
attrs[attrMatch[1]] = attrMatch[3]
|
||||
}
|
||||
const attrEntries = Object.entries(attrs)
|
||||
.map(([k, v]) => `'${escapeJS(k)}': '${escapeJS(v)}'`)
|
||||
.join(', ')
|
||||
children.push(`_h('${tag}', _v2 ? { attrs: { ${attrEntries} } } : { ${attrEntries} })`)
|
||||
}
|
||||
|
||||
const pathElements = paths
|
||||
.map((d) => {
|
||||
const escaped = escapeJS(d)
|
||||
return `h('path', isVue2 ? { attrs: { d: '${escaped}' } } : { d: '${escaped}' })`
|
||||
})
|
||||
.join(', ')
|
||||
const pathElements = children.join(', ')
|
||||
|
||||
const safeViewBox = escapeJS(viewBox)
|
||||
|
||||
return `import { h, isVue2 } from 'vue-demi'
|
||||
// Icon functions accept optional (h, v2) from OsIcon. When OsIcon passes
|
||||
// Vue 2's $createElement, we use it directly — avoiding the globally-imported
|
||||
// h() which requires currentInstance in Vue 2.7.
|
||||
// When used as a standalone Vue 3 component (e.g. in Storybook), h/v2 are not
|
||||
// functions/booleans, so we fall back to the imported _hImport / _v2Import.
|
||||
return `import { h as _hImport, isVue2 as _v2Import } from 'vue-demi'
|
||||
const svgAttrs = { xmlns: 'http://www.w3.org/2000/svg', viewBox: '${safeViewBox}', fill: 'currentColor' }
|
||||
export default () => h('svg', isVue2 ? { attrs: svgAttrs } : svgAttrs, [${pathElements}])
|
||||
export default (h, v2) => { const _h = typeof h === 'function' ? h : _hImport; const _v2 = typeof v2 === 'boolean' ? v2 : _v2Import; return _h('svg', _v2 ? { attrs: svgAttrs } : svgAttrs, [${pathElements}]) }
|
||||
`
|
||||
},
|
||||
}
|
||||
|
||||
9
packages/ui/src/test/setup.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { config } from '@vue/test-utils'
|
||||
|
||||
config.global.config.warnHandler = (msg) => {
|
||||
throw new Error(`[Vue warn]: ${msg}`)
|
||||
}
|
||||
|
||||
config.global.config.errorHandler = (err) => {
|
||||
throw err instanceof Error ? err : new Error(`[Vue error]: ${String(err)}`)
|
||||
}
|
||||
@ -68,6 +68,7 @@ export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['src/test/setup.ts'],
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
exclude: ['src/**/*.visual.spec.ts', 'src/plugins/**'],
|
||||
coverage: {
|
||||
|
||||
0
webapp/assets/_new/icons/svgs/.gitkeep
Normal file
@ -1,5 +0,0 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>angle-down</title>
|
||||
<path d="M4.219 10.781l11.781 11.781 11.781-11.781 1.438 1.438-12.5 12.5-0.719 0.688-0.719-0.688-12.5-12.5z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 281 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1080" height="1080"><rect width="100%" height="100%" fill="transparent"/><path d="M15 3a2 2 0 0 1 2 2c0 .085-.021.168-.031.25C20.49 6.174 23 9.523 23 13.281V22c0 .565.435 1 1 1h1v2h-7.188c.114.316.188.647.188 1 0 1.645-1.355 3-3 3s-3-1.355-3-3c0-.353.073-.684.188-1H5v-2h1c.565 0 1-.435 1-1v-9c0-3.726 2.574-6.866 6.031-7.75C13.021 5.168 13 5.085 13 5a2 2 0 0 1 2-2zm-.437 4A6.004 6.004 0 0 0 9 13v9c0 .353-.073.684-.188 1h12.375a2.925 2.925 0 0 1-.188-1v-8.719c0-3.319-2.546-6.183-5.813-6.281-.064-.002-.124 0-.188 0-.148 0-.292-.011-.438 0zM15 25c-.564 0-1 .436-1 1 0 .564.436 1 1 1 .564 0 1-.436 1-1 0-.564-.436-1-1-1z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#000;fill-rule:nonzero;opacity:1" transform="translate(33.75) scale(33.75)"/><rect width="74.334" height="74.334" x="-37.167" y="-37.167" rx="0" ry="0" style="stroke:#000;stroke-width:0;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#000;fill-rule:nonzero;opacity:1" transform="matrix(9.42 -12.59 .8 .6 538.54 541.95)" vector-effect="non-scaling-stroke"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,5 +0,0 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>book</title>
|
||||
<path d="M10 5c2.92 0 5.482 0.981 6 1.188 0.518-0.206 3.080-1.188 6-1.188 3.227 0 6.375 1.313 6.375 1.313l0.625 0.281v20.406h-11.281c-0.346 0.597-0.979 1-1.719 1s-1.373-0.403-1.719-1h-11.281v-20.406l0.625-0.281s3.148-1.313 6.375-1.313zM10 7c-2.199 0-4.232 0.69-5 0.969v16.125c1.188-0.392 2.897-0.875 5-0.875 2.057 0 3.888 0.506 5 0.875v-16.125c-1-0.343-3.067-0.969-5-0.969zM22 7c-1.933 0-4 0.626-5 0.969v16.125c1.112-0.369 2.943-0.875 5-0.875 2.103 0 3.813 0.483 5 0.875v-16.125c-0.768-0.279-2.801-0.969-5-0.969z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 680 B |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>bullhorn</title>
|
||||
<path d="M28 3.031v9.156c1.156 0.418 2 1.521 2 2.813s-0.844 2.394-2 2.813v9.156l-1.594-1.156s-2.007-1.443-4.875-2.906-6.587-2.906-9.813-2.906h-3.375l1.625 5.719 0.344 1.281h-6.063l-0.219-0.719-2-7-0.031-0.156v-9.125h9.719c3.27 0 6.987-1.412 9.844-2.875s4.844-2.938 4.844-2.938zM26 6.75c-0.82 0.558-1.459 1.064-3.531 2.125-2.929 1.5-6.726 3.050-10.469 3.125v6c3.708 0.073 7.499 1.595 10.438 3.094 2.078 1.060 2.735 1.567 3.563 2.125v-16.469zM4 12v6h6v-6h-6zM4.344 20l1.406 5h1.906l-1.406-5h-1.906z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>bullhorn</title><path d="M28 3.031v9.156c1.156 0.418 2 1.521 2 2.813s-0.844 2.394-2 2.813v9.156l-1.594-1.156s-2.007-1.443-4.875-2.906-6.587-2.906-9.813-2.906h-3.375l1.625 5.719 0.344 1.281h-6.063l-0.219-0.719-2-7-0.031-0.156v-9.125h9.719c3.27 0 6.987-1.412 9.844-2.875s4.844-2.938 4.844-2.938zM26 6.75c-0.82 0.558-1.459 1.064-3.531 2.125-2.929 1.5-6.726 3.050-10.469 3.125v6c3.708 0.073 7.499 1.595 10.438 3.094 2.078 1.060 2.735 1.567 3.563 2.125v-16.469zM4 12v6h6v-6h-6zM4.344 20l1.406 5h1.906l-1.406-5h-1.906z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 668 B After Width: | Height: | Size: 664 B |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>camera</title>
|
||||
<path d="M11.5 6h9l0.313 0.406 1.188 1.594h7v18h-26v-18h7l1.188-1.594zM12.5 8l-1.188 1.594-0.313 0.406h-6v14h22v-14h-6l-0.313-0.406-1.188-1.594h-7zM8 11c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM16 11c3.302 0 6 2.698 6 6s-2.698 6-6 6-6-2.698-6-6 2.698-6 6-6zM16 13c-2.221 0-4 1.779-4 4s1.779 4 4 4 4-1.779 4-4-1.779-4-4-4z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>camera</title><path d="M11.5 6h9l0.313 0.406 1.188 1.594h7v18h-26v-18h7l1.188-1.594zM12.5 8l-1.188 1.594-0.313 0.406h-6v14h22v-14h-6l-0.313-0.406-1.188-1.594h-7zM8 11c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM16 11c3.302 0 6 2.698 6 6s-2.698 6-6 6-6-2.698-6-6 2.698-6 6-6zM16 13c-2.221 0-4 1.779-4 4s1.779 4 4 4 4-1.779 4-4-1.779-4-4-4z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 508 B After Width: | Height: | Size: 504 B |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>child</title>
|
||||
<path d="M12 3c2.202 0 3.791 1.007 4.531 2.313 0.026-0.041 0.034-0.084 0.063-0.125 0.453-0.641 1.315-1.188 2.406-1.188v2c-0.453 0-0.588 0.111-0.719 0.281 3.845 0.921 6.812 4.105 7.563 8.063 1.193 0.397 2.156 1.337 2.156 2.656 0 1.365-1.024 2.33-2.281 2.688-0.816 4.701-4.82 8.313-9.719 8.313s-8.903-3.611-9.719-8.313c-1.257-0.357-2.281-1.323-2.281-2.688s1.024-2.33 2.281-2.688c0.741-4.271 4.122-7.637 8.406-8.219-0.39-0.574-1.192-1.094-2.688-1.094v-2zM16 8c-4.093 0-7.461 3.121-7.906 7.125l-0.094 0.875h-1c-0.555 0-1 0.445-1 1s0.445 1 1 1h1l0.094 0.875c0.445 4.004 3.813 7.125 7.906 7.125s7.461-3.121 7.906-7.125l0.094-0.875h1c0.555 0 1-0.445 1-1s-0.445-1-1-1h-0.875l-0.125-0.875c-0.536-4.019-3.907-7.125-8-7.125zM12.5 16c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5zM19.5 16c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>child</title><path d="M12 3c2.202 0 3.791 1.007 4.531 2.313 0.026-0.041 0.034-0.084 0.063-0.125 0.453-0.641 1.315-1.188 2.406-1.188v2c-0.453 0-0.588 0.111-0.719 0.281 3.845 0.921 6.812 4.105 7.563 8.063 1.193 0.397 2.156 1.337 2.156 2.656 0 1.365-1.024 2.33-2.281 2.688-0.816 4.701-4.82 8.313-9.719 8.313s-8.903-3.611-9.719-8.313c-1.257-0.357-2.281-1.323-2.281-2.688s1.024-2.33 2.281-2.688c0.741-4.271 4.122-7.637 8.406-8.219-0.39-0.574-1.192-1.094-2.688-1.094v-2zM16 8c-4.093 0-7.461 3.121-7.906 7.125l-0.094 0.875h-1c-0.555 0-1 0.445-1 1s0.445 1 1 1h1l0.094 0.875c0.445 4.004 3.813 7.125 7.906 7.125s7.461-3.121 7.906-7.125l0.094-0.875h1c0.555 0 1-0.445 1-1s-0.445-1-1-1h-0.875l-0.125-0.875c-0.536-4.019-3.907-7.125-8-7.125zM12.5 16c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5zM19.5 16c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,5 +0,0 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>comments</title>
|
||||
<path d="M2 5h20v16h-9.656l-4.719 3.781-1.625 1.313v-5.094h-4v-16zM4 7v12h4v2.906l3.375-2.688 0.281-0.219h8.344v-12h-16zM24 9h6v16h-4v5.094l-6.344-5.094h-9.313l2.5-2h7.5l3.656 2.906v-2.906h4v-12h-4v-2z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 373 B |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>credit-card</title>
|
||||
<path d="M5 5h18c1.645 0 3 1.355 3 3v1h1c1.645 0 3 1.355 3 3v12c0 1.645-1.355 3-3 3h-18c-1.645 0-3-1.355-3-3v-1h-1c-1.645 0-3-1.355-3-3v-12c0-1.645 1.355-3 3-3zM5 7c-0.565 0-1 0.435-1 1v12c0 0.565 0.435 1 1 1h18c0.565 0 1-0.435 1-1v-9h-19v-2h19v-1c0-0.565-0.435-1-1-1h-18zM26 11v2h2v-1c0-0.565-0.435-1-1-1h-1zM26 15v5c0 1.645-1.355 3-3 3h-15v1c0 0.565 0.435 1 1 1h18c0.565 0 1-0.435 1-1v-9h-2z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>credit-card</title><path d="M5 5h18c1.645 0 3 1.355 3 3v1h1c1.645 0 3 1.355 3 3v12c0 1.645-1.355 3-3 3h-18c-1.645 0-3-1.355-3-3v-1h-1c-1.645 0-3-1.355-3-3v-12c0-1.645 1.355-3 3-3zM5 7c-0.565 0-1 0.435-1 1v12c0 0.565 0.435 1 1 1h18c0.565 0 1-0.435 1-1v-9h-19v-2h19v-1c0-0.565-0.435-1-1-1h-18zM26 11v2h2v-1c0-0.565-0.435-1-1-1h-1zM26 15v5c0 1.645-1.355 3-3 3h-15v1c0 0.565 0.435 1 1 1h18c0.565 0 1-0.435 1-1v-9h-2z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 568 B After Width: | Height: | Size: 564 B |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cubes</title>
|
||||
<path d="M16 4l0.375 0.156 6.625 2.656v6.719l5.406 2.344 0.594 0.281v8.063l-0.5 0.313-6 3.344-0.469 0.25-0.469-0.219-5.563-2.781-5.563 2.781-0.469 0.219-0.469-0.25-6-3.344-0.5-0.313v-8.063l0.594-0.281 5.406-2.344v-6.719l6.625-2.656zM16 6.188l-3.281 1.281 3.281 1.281 3.281-1.281zM11 8.938v4.625l4 1.781v-4.875zM21 8.938l-4 1.531v4.875l4-1.781v-4.625zM10 15.313l-3.625 1.563 3.625 1.813 3.625-1.781zM22 15.313l-2.5 1.094-1.125 0.5 3.625 1.781 3.625-1.813zM5 18.406v4.656l4 2.25v-4.906zM27 18.406l-4 2v4.906l4-2.25v-4.656zM15 18.469l-4 1.938v4.969l4-2v-4.906zM17 18.469v4.906l4 2v-4.969z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>cubes</title><path d="M16 4l0.375 0.156 6.625 2.656v6.719l5.406 2.344 0.594 0.281v8.063l-0.5 0.313-6 3.344-0.469 0.25-0.469-0.219-5.563-2.781-5.563 2.781-0.469 0.219-0.469-0.25-6-3.344-0.5-0.313v-8.063l0.594-0.281 5.406-2.344v-6.719l6.625-2.656zM16 6.188l-3.281 1.281 3.281 1.281 3.281-1.281zM11 8.938v4.625l4 1.781v-4.875zM21 8.938l-4 1.531v4.875l4-1.781v-4.625zM10 15.313l-3.625 1.563 3.625 1.813 3.625-1.781zM22 15.313l-2.5 1.094-1.125 0.5 3.625 1.781 3.625-1.813zM5 18.406v4.656l4 2.25v-4.906zM27 18.406l-4 2v4.906l4-2.25v-4.656zM15 18.469l-4 1.938v4.969l4-2v-4.906zM17 18.469v4.906l4 2v-4.969z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 754 B After Width: | Height: | Size: 750 B |
@ -1,20 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M5.9,25.1c-0.3,0.3-0.3,0.8,0,1.1s0.8,0.3,1.1,0c1.7-1.7,4.5-1.7,6.3,0c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2
|
||||
c0.3-0.3,0.3-0.8,0-1.1C12,22.8,8.2,22.8,5.9,25.1z"/>
|
||||
<path d="M24.4,8.7c0.7-0.7,2-0.7,2.7,0C27.2,8.9,27.4,9,27.6,9c0.2,0,0.4-0.1,0.5-0.2c0.3-0.3,0.3-0.8,0-1.1
|
||||
c-1.3-1.3-3.5-1.3-4.8,0C23,8,23,8.5,23.3,8.7C23.6,9,24.1,9,24.4,8.7z"/>
|
||||
<path d="M16.4,7.7c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0c0.7-0.7,1.9-0.7,2.7,0C20.3,8.9,20.5,9,20.7,9s0.4-0.1,0.5-0.2
|
||||
c0.3-0.3,0.3-0.8,0-1.1C19.9,6.4,17.7,6.4,16.4,7.7z"/>
|
||||
<path d="M31.4,0.8c-0.2-0.1-0.5-0.2-0.7-0.1c-2,0.8-5.1,1.2-8.4,1.2s-6.4-0.4-8.4-1.2c-0.2-0.1-0.5-0.1-0.7,0.1
|
||||
c-0.2,0.1-0.3,0.4-0.3,0.6v9.9c-1,0.1-1.9,0.1-3,0.1c-3.3,0-6.4-0.4-8.4-1.2c-0.2-0.1-0.5-0.1-0.7,0.1c-0.2,0.1-0.3,0.4-0.3,0.6
|
||||
l0,11.5c0,5.2,4.2,9.4,9.4,9.4s9.4-4.2,9.4-9.4V22c0.7,0.3,1.6,0.4,3,0.4c5.2,0,9.4-4.2,9.4-9.4V1.4C31.7,1.2,31.6,0.9,31.4,0.8z
|
||||
M9.9,30.4c-4.4,0-7.9-3.6-7.9-7.9V12c2.1,0.6,4.9,1,7.9,1c2.7,0,5.2-0.3,7.3-0.8l0.5-0.1c0,1.6,0,2.9,0,4c0,1.3,0,2.4,0.1,3.2v3.2
|
||||
C17.8,26.8,14.2,30.4,9.9,30.4z M30.2,13c0,4.4-3.6,7.9-7.9,7.9c-2.1,0-2.8,0-3-1.7v-2.8c0.9,0.5,1.9,0.8,3,0.8
|
||||
c1.6,0,3.1-0.6,4.2-1.7c0.3-0.3,0.3-0.8,0-1.1s-0.8-0.3-1.1,0c-0.8,0.8-2,1.3-3.1,1.3c-1.1,0-2.1-0.4-3-1.2v-3.6
|
||||
c0-0.2-0.1-0.5-0.3-0.6c-0.2-0.1-0.5-0.2-0.7-0.1c-0.4,0.2-0.9,0.3-1.4,0.4l-2.5,0.4V2.5c2.1,0.6,4.9,1,7.9,1c3,0,5.8-0.3,7.9-1V13
|
||||
z"/>
|
||||
<path d="M10.9,17.7c-0.3,0.3-0.3,0.8,0,1.1s0.8,0.3,1.1,0c0.7-0.7,2-0.7,2.7,0c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2
|
||||
c0.3-0.3,0.3-0.8,0-1.1C14.4,16.4,12.2,16.4,10.9,17.7z"/>
|
||||
<path d="M7.7,18.7C7.9,18.9,8.1,19,8.3,19s0.4-0.1,0.5-0.2c0.3-0.3,0.3-0.8,0-1.1c-1.3-1.3-3.5-1.3-4.8,0c-0.3,0.3-0.3,0.8,0,1.1
|
||||
s0.8,0.3,1.1,0C5.8,18,7,18,7.7,18.7z"/>
|
||||
</svg>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32"><path d="M5.9,25.1c-0.3,0.3-0.3,0.8,0,1.1s0.8,0.3,1.1,0c1.7-1.7,4.5-1.7,6.3,0c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2 c0.3-0.3,0.3-0.8,0-1.1C12,22.8,8.2,22.8,5.9,25.1z"/><path d="M24.4,8.7c0.7-0.7,2-0.7,2.7,0C27.2,8.9,27.4,9,27.6,9c0.2,0,0.4-0.1,0.5-0.2c0.3-0.3,0.3-0.8,0-1.1 c-1.3-1.3-3.5-1.3-4.8,0C23,8,23,8.5,23.3,8.7C23.6,9,24.1,9,24.4,8.7z"/><path d="M16.4,7.7c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0c0.7-0.7,1.9-0.7,2.7,0C20.3,8.9,20.5,9,20.7,9s0.4-0.1,0.5-0.2 c0.3-0.3,0.3-0.8,0-1.1C19.9,6.4,17.7,6.4,16.4,7.7z"/><path d="M31.4,0.8c-0.2-0.1-0.5-0.2-0.7-0.1c-2,0.8-5.1,1.2-8.4,1.2s-6.4-0.4-8.4-1.2c-0.2-0.1-0.5-0.1-0.7,0.1 c-0.2,0.1-0.3,0.4-0.3,0.6v9.9c-1,0.1-1.9,0.1-3,0.1c-3.3,0-6.4-0.4-8.4-1.2c-0.2-0.1-0.5-0.1-0.7,0.1c-0.2,0.1-0.3,0.4-0.3,0.6 l0,11.5c0,5.2,4.2,9.4,9.4,9.4s9.4-4.2,9.4-9.4V22c0.7,0.3,1.6,0.4,3,0.4c5.2,0,9.4-4.2,9.4-9.4V1.4C31.7,1.2,31.6,0.9,31.4,0.8z M9.9,30.4c-4.4,0-7.9-3.6-7.9-7.9V12c2.1,0.6,4.9,1,7.9,1c2.7,0,5.2-0.3,7.3-0.8l0.5-0.1c0,1.6,0,2.9,0,4c0,1.3,0,2.4,0.1,3.2v3.2 C17.8,26.8,14.2,30.4,9.9,30.4z M30.2,13c0,4.4-3.6,7.9-7.9,7.9c-2.1,0-2.8,0-3-1.7v-2.8c0.9,0.5,1.9,0.8,3,0.8 c1.6,0,3.1-0.6,4.2-1.7c0.3-0.3,0.3-0.8,0-1.1s-0.8-0.3-1.1,0c-0.8,0.8-2,1.3-3.1,1.3c-1.1,0-2.1-0.4-3-1.2v-3.6 c0-0.2-0.1-0.5-0.3-0.6c-0.2-0.1-0.5-0.2-0.7-0.1c-0.4,0.2-0.9,0.3-1.4,0.4l-2.5,0.4V2.5c2.1,0.6,4.9,1,7.9,1c3,0,5.8-0.3,7.9-1V13 z"/><path d="M10.9,17.7c-0.3,0.3-0.3,0.8,0,1.1s0.8,0.3,1.1,0c0.7-0.7,2-0.7,2.7,0c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2 c0.3-0.3,0.3-0.8,0-1.1C14.4,16.4,12.2,16.4,10.9,17.7z"/><path d="M7.7,18.7C7.9,18.9,8.1,19,8.3,19s0.4-0.1,0.5-0.2c0.3-0.3,0.3-0.8,0-1.1c-1.3-1.3-3.5-1.3-4.8,0c-0.3,0.3-0.3,0.8,0,1.1 s0.8,0.3,1.1,0C5.8,18,7,18,7.7,18.7z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@ -1,14 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M27.6,19.7L26.8,19c-3-2.8-4.8-4.4-5.2-4.6c-0.9-0.6-2.9-0.5-3.6-0.4c-0.2-0.5-0.5-0.9-0.9-1.2V2.2
|
||||
c0-1-0.7-1.6-1.3-1.6c-0.6-0.1-1.4,0.4-1.6,1.4c-0.1,0.5-0.3,1.4-0.5,2.4c-0.6,2.9-1,5-1,5.4c-0.1,1.1,1.1,2.8,1.5,3.3
|
||||
c-0.3,0.4-0.5,0.9-0.5,1.4c0,0,0,0,0,0.1c-0.9,0.6-7.3,5-8.7,6c-0.8,0.6-0.9,1.4-0.6,2c0.2,0.4,0.7,0.7,1.2,0.7
|
||||
c0.3,0,0.5-0.1,0.8-0.2c1.5-0.6,6.7-2.9,7.4-3.2c0.2-0.1,0.4-0.3,0.6-0.5l-1.3,11c0,1,2.3,1,2.8,1s2.8,0,2.8-1.1l-1.4-13.5
|
||||
c1.8,1.1,7.2,4.4,8.6,5.2c0.3,0.2,0.7,0.3,0.9,0.3c0.5,0,0.9-0.2,1.1-0.5C28.5,21.4,28.5,20.5,27.6,19.7z M14.8,14.6
|
||||
c0-0.8,0.6-1.2,1.2-1.2c0.6,0,1.2,0.6,1.2,1.2c0,0.8-0.6,1.2-1.2,1.2C15.2,15.8,14.8,15.2,14.8,14.6z M14.8,4.7c0.2-1,0.4-2,0.5-2.5
|
||||
c0.1-0.5,0.3-0.7,0.5-0.6c0.2,0,0.4,0.2,0.4,0.6v10.2c-0.1,0-0.1,0-0.2,0c-0.3,0-0.6,0.1-0.9,0.2c-0.6-0.8-1.4-2-1.3-2.7
|
||||
C13.8,9.5,14.4,6.5,14.8,4.7z M13.5,19.1c-0.6,0.3-5.8,2.6-7.3,3.2c-0.4,0.2-0.7,0.1-0.8-0.1c-0.1-0.2,0-0.4,0.3-0.7
|
||||
c1.3-0.9,6.9-4.8,8.4-5.8c0.2,0.3,0.5,0.6,0.8,0.8C14.6,17.4,13.9,18.8,13.5,19.1z M16,30.4c-0.6,0-1.4-0.1-1.8-0.2l1.6-13.4
|
||||
c0,0,0.1,0,0.1,0c0.1,0,0.3,0,0.4,0l1.4,13.4C17.4,30.3,16.6,30.4,16,30.4z M27.3,21.3c-0.1,0.1-0.4,0.2-0.7,0
|
||||
c-1.5-0.9-7.2-4.4-8.8-5.3c0.2-0.3,0.3-0.6,0.4-0.9c1-0.1,2.5-0.1,3,0.3c0.4,0.2,3.6,3.2,5,4.5l0.8,0.7
|
||||
C27.3,20.8,27.4,21.1,27.3,21.3z"/>
|
||||
</svg>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32"><path d="M27.6,19.7L26.8,19c-3-2.8-4.8-4.4-5.2-4.6c-0.9-0.6-2.9-0.5-3.6-0.4c-0.2-0.5-0.5-0.9-0.9-1.2V2.2 c0-1-0.7-1.6-1.3-1.6c-0.6-0.1-1.4,0.4-1.6,1.4c-0.1,0.5-0.3,1.4-0.5,2.4c-0.6,2.9-1,5-1,5.4c-0.1,1.1,1.1,2.8,1.5,3.3 c-0.3,0.4-0.5,0.9-0.5,1.4c0,0,0,0,0,0.1c-0.9,0.6-7.3,5-8.7,6c-0.8,0.6-0.9,1.4-0.6,2c0.2,0.4,0.7,0.7,1.2,0.7 c0.3,0,0.5-0.1,0.8-0.2c1.5-0.6,6.7-2.9,7.4-3.2c0.2-0.1,0.4-0.3,0.6-0.5l-1.3,11c0,1,2.3,1,2.8,1s2.8,0,2.8-1.1l-1.4-13.5 c1.8,1.1,7.2,4.4,8.6,5.2c0.3,0.2,0.7,0.3,0.9,0.3c0.5,0,0.9-0.2,1.1-0.5C28.5,21.4,28.5,20.5,27.6,19.7z M14.8,14.6 c0-0.8,0.6-1.2,1.2-1.2c0.6,0,1.2,0.6,1.2,1.2c0,0.8-0.6,1.2-1.2,1.2C15.2,15.8,14.8,15.2,14.8,14.6z M14.8,4.7c0.2-1,0.4-2,0.5-2.5 c0.1-0.5,0.3-0.7,0.5-0.6c0.2,0,0.4,0.2,0.4,0.6v10.2c-0.1,0-0.1,0-0.2,0c-0.3,0-0.6,0.1-0.9,0.2c-0.6-0.8-1.4-2-1.3-2.7 C13.8,9.5,14.4,6.5,14.8,4.7z M13.5,19.1c-0.6,0.3-5.8,2.6-7.3,3.2c-0.4,0.2-0.7,0.1-0.8-0.1c-0.1-0.2,0-0.4,0.3-0.7 c1.3-0.9,6.9-4.8,8.4-5.8c0.2,0.3,0.5,0.6,0.8,0.8C14.6,17.4,13.9,18.8,13.5,19.1z M16,30.4c-0.6,0-1.4-0.1-1.8-0.2l1.6-13.4 c0,0,0.1,0,0.1,0c0.1,0,0.3,0,0.4,0l1.4,13.4C17.4,30.3,16.6,30.4,16,30.4z M27.3,21.3c-0.1,0.1-0.4,0.2-0.7,0 c-1.5-0.9-7.2-4.4-8.8-5.3c0.2-0.3,0.3-0.6,0.4-0.9c1-0.1,2.5-0.1,3,0.3c0.4,0.2,3.6,3.2,5,4.5l0.8,0.7 C27.3,20.8,27.4,21.1,27.3,21.3z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -1,13 +1 @@
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<rect x="8.1" y="13.3" width="1.5" height="1.9"/>
|
||||
<path d="M31.9,17.9L31.9,17.9l0-0.6c-0.2-5.5-4.1-10.3-9.5-11.6c-0.2,0-0.8-0.2-0.8-0.2c-3.1-0.6-6.3-0.5-9.5,0.1
|
||||
c-0.3,0.1-0.6,0.1-0.9,0.2C10.6,5.7,10,5.4,9.7,5.4L9.5,5.3C8.7,5,7.6,4.6,6.8,5.1C6,5.7,6,6.7,6.2,7.3l0.3,1.2
|
||||
C5.1,9.7,4,11.2,3.3,12.8c-0.4,0.8-0.8,0.8-1.2,0.8c-0.1,0-0.2,0-0.3,0H0l0,5.5l0.6,0.1c0,0,1.1,0.2,1.6,1C2.5,20.7,2.7,21,3,22.3
|
||||
c1.1,3.4,3.6,5.3,4.6,5.9l0,3.9H12l2.6-3.4h6.1l1.7,3.4h4.9v-4.6l0.4-0.4c2.5-2.2,3.9-5.2,4.1-8.5l0-0.4
|
||||
C31.9,18.1,31.9,18,31.9,17.9z M26.7,25.9l-0.9,0.9v3.8h-2.5l-1.7-3.4h-7.7l-2.6,3.4H9.1l0-3.2l-0.4-0.2c0,0-3.1-1.7-4.2-5.3
|
||||
c-0.4-1.4-0.6-1.8-1-2.3c-0.5-0.9-1.4-1.3-2-1.5v-2.8l0.6,0c0.5,0,1.8,0,2.6-1.7c0.5-1.2,1.3-2.3,2.2-3.3c0.8-0.8,1.3-1,1.1-1.5
|
||||
l0,0L7.6,7c-0.1-0.2-0.1-0.5,0-0.6c0.2-0.1,1,0.1,1.4,0.3l0.3,0.1c0.3,0.1,0.9,0.4,1.7,0.6l0.2,0.1l0.3-0.1c0.3-0.1,0.7-0.2,1-0.2
|
||||
c3-0.6,5.9-0.6,8.9-0.1c0,0,0.6,0.1,0.8,0.1c4.7,1.1,8.2,5.3,8.4,10.1l0,0.8c-0.1,1.3-1.2,2.4-2.5,2.4c-1,0-1.9-0.9-1.9-2
|
||||
c0-0.8,0.6-1.4,1.4-1.4v-1.5c-1.6,0-2.9,1.3-2.9,2.9c0,1.9,1.5,3.5,3.4,3.5c0.7,0,1.3-0.2,1.8-0.5C29.1,23.2,28.1,24.7,26.7,25.9z"
|
||||
/>
|
||||
</svg>
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32"><rect x="8.1" y="13.3" width="1.5" height="1.9"/><path d="M31.9,17.9L31.9,17.9l0-0.6c-0.2-5.5-4.1-10.3-9.5-11.6c-0.2,0-0.8-0.2-0.8-0.2c-3.1-0.6-6.3-0.5-9.5,0.1 c-0.3,0.1-0.6,0.1-0.9,0.2C10.6,5.7,10,5.4,9.7,5.4L9.5,5.3C8.7,5,7.6,4.6,6.8,5.1C6,5.7,6,6.7,6.2,7.3l0.3,1.2 C5.1,9.7,4,11.2,3.3,12.8c-0.4,0.8-0.8,0.8-1.2,0.8c-0.1,0-0.2,0-0.3,0H0l0,5.5l0.6,0.1c0,0,1.1,0.2,1.6,1C2.5,20.7,2.7,21,3,22.3 c1.1,3.4,3.6,5.3,4.6,5.9l0,3.9H12l2.6-3.4h6.1l1.7,3.4h4.9v-4.6l0.4-0.4c2.5-2.2,3.9-5.2,4.1-8.5l0-0.4 C31.9,18.1,31.9,18,31.9,17.9z M26.7,25.9l-0.9,0.9v3.8h-2.5l-1.7-3.4h-7.7l-2.6,3.4H9.1l0-3.2l-0.4-0.2c0,0-3.1-1.7-4.2-5.3 c-0.4-1.4-0.6-1.8-1-2.3c-0.5-0.9-1.4-1.3-2-1.5v-2.8l0.6,0c0.5,0,1.8,0,2.6-1.7c0.5-1.2,1.3-2.3,2.2-3.3c0.8-0.8,1.3-1,1.1-1.5 l0,0L7.6,7c-0.1-0.2-0.1-0.5,0-0.6c0.2-0.1,1,0.1,1.4,0.3l0.3,0.1c0.3,0.1,0.9,0.4,1.7,0.6l0.2,0.1l0.3-0.1c0.3-0.1,0.7-0.2,1-0.2 c3-0.6,5.9-0.6,8.9-0.1c0,0,0.6,0.1,0.8,0.1c4.7,1.1,8.2,5.3,8.4,10.1l0,0.8c-0.1,1.3-1.2,2.4-2.5,2.4c-1,0-1.9-0.9-1.9-2 c0-0.8,0.6-1.4,1.4-1.4v-1.5c-1.6,0-2.9,1.3-2.9,2.9c0,1.9,1.5,3.5,3.4,3.5c0.7,0,1.3-0.2,1.8-0.5C29.1,23.2,28.1,24.7,26.7,25.9z" /></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>graduation-cap</title>
|
||||
<path d="M16 6.781l0.313 0.094 15.344 5.125-2.844 0.938-2.813 0.938v5.125c0 0.82-0.499 1.5-1.094 1.969s-1.332 0.798-2.219 1.094c-1.773 0.591-4.112 0.938-6.688 0.938s-4.914-0.346-6.688-0.938c-0.887-0.296-1.624-0.625-2.219-1.094s-1.094-1.149-1.094-1.969v-5.125l-2-0.656v8.063c0.597 0.346 1 0.979 1 1.719 0 1.105-0.895 2-2 2s-2-0.895-2-2c0-0.74 0.403-1.373 1-1.719v-8.75l-1.656-0.531 2.844-0.938 12.5-4.188zM16 8.875l-9.375 3.125 9.375 3.125 9.375-3.125zM8 14.563v4.438c0 0.009-0.004 0.126 0.313 0.375s0.883 0.565 1.625 0.813c1.484 0.495 3.667 0.813 6.063 0.813s4.579-0.318 6.063-0.813c0.742-0.247 1.309-0.563 1.625-0.813s0.313-0.366 0.313-0.375v-4.438l-7.688 2.563-0.313 0.094-0.313-0.094z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>graduation-cap</title><path d="M16 6.781l0.313 0.094 15.344 5.125-2.844 0.938-2.813 0.938v5.125c0 0.82-0.499 1.5-1.094 1.969s-1.332 0.798-2.219 1.094c-1.773 0.591-4.112 0.938-6.688 0.938s-4.914-0.346-6.688-0.938c-0.887-0.296-1.624-0.625-2.219-1.094s-1.094-1.149-1.094-1.969v-5.125l-2-0.656v8.063c0.597 0.346 1 0.979 1 1.719 0 1.105-0.895 2-2 2s-2-0.895-2-2c0-0.74 0.403-1.373 1-1.719v-8.75l-1.656-0.531 2.844-0.938 12.5-4.188zM16 8.875l-9.375 3.125 9.375 3.125 9.375-3.125zM8 14.563v4.438c0 0.009-0.004 0.126 0.313 0.375s0.883 0.565 1.625 0.813c1.484 0.495 3.667 0.813 6.063 0.813s4.579-0.318 6.063-0.813c0.742-0.247 1.309-0.563 1.625-0.813s0.313-0.366 0.313-0.375v-4.438l-7.688 2.563-0.313 0.094-0.313-0.094z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 861 B |
@ -1,5 +0,0 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-pointer-o</title>
|
||||
<path d="M13 2c1.645 0 3 1.355 3 3v4.188c0.316-0.114 0.647-0.188 1-0.188 0.767 0 1.467 0.3 2 0.781 0.533-0.481 1.233-0.781 2-0.781 1.395 0 2.578 0.982 2.906 2.281 0.368-0.163 0.762-0.281 1.188-0.281 1.645 0 3 1.355 3 3v7.813c0 4.533-3.654 8.188-8.188 8.188h-1.719c-1.935 0-3.651-0.675-5-1.688l-0.031-0.063-0.063-0.031-8.188-8.094v-0.031c-1.154-1.154-1.154-3.034 0-4.188s3.034-1.154 4.188 0l0.25 0.219 0.656 0.688v-11.813c0-1.645 1.355-3 3-3zM13 4c-0.555 0-1 0.445-1 1v16.625l-4.313-4.313c-0.446-0.446-0.929-0.446-1.375 0s-0.446 0.929 0 1.375l8.094 8c1.051 0.788 2.317 1.313 3.781 1.313h1.719c3.467 0 6.188-2.721 6.188-6.188v-7.813c0-0.555-0.445-1-1-1s-1 0.445-1 1v2h-2.094v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-11c0-0.555-0.445-1-1-1z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 957 B |
@ -1,7 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M26.1,20.7c-2.9,5.1-9.3,8.8-10.8,9.6c-2.1-1.1-13.7-7.8-13.7-16.1c0-3.6,2.8-6.5,6.5-6.5c2.6,0,4.9,1.5,5.9,3.9l0.7,1.7
|
||||
l0.7-1.7c1-2.4,3.3-3.9,5.9-3.9c3.6,0,6.5,2.8,6.5,6.5V15h1.5v-0.8c0-4.5-3.5-8-8-8c-2.7,0-5.2,1.3-6.6,3.5
|
||||
c-1.4-2.2-3.9-3.5-6.6-3.5c-4.5,0-8,3.5-8,8c0,10,14.3,17.3,14.9,17.6l0.3,0.2l0.3-0.2c0.3-0.2,8.3-4.2,11.8-10.4l0.4-0.7L26.5,20
|
||||
L26.1,20.7z"/>
|
||||
<polygon points="22.9,17 19.5,21.2 17,14.5 11.6,19.9 12.6,20.9 16.4,17.2 19,24.2 23.6,18.5 31.9,18.5 31.9,17 "/>
|
||||
</svg>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32"><path d="M26.1,20.7c-2.9,5.1-9.3,8.8-10.8,9.6c-2.1-1.1-13.7-7.8-13.7-16.1c0-3.6,2.8-6.5,6.5-6.5c2.6,0,4.9,1.5,5.9,3.9l0.7,1.7 l0.7-1.7c1-2.4,3.3-3.9,5.9-3.9c3.6,0,6.5,2.8,6.5,6.5V15h1.5v-0.8c0-4.5-3.5-8-8-8c-2.7,0-5.2,1.3-6.6,3.5 c-1.4-2.2-3.9-3.5-6.6-3.5c-4.5,0-8,3.5-8,8c0,10,14.3,17.3,14.9,17.6l0.3,0.2l0.3-0.2c0.3-0.2,8.3-4.2,11.8-10.4l0.4-0.7L26.5,20 L26.1,20.7z"/><polygon points="22.9,17 19.5,21.2 17,14.5 11.6,19.9 12.6,20.9 16.4,17.2 19,24.2 23.6,18.5 31.9,18.5 31.9,17 "/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 625 B After Width: | Height: | Size: 606 B |
@ -1,5 +0,0 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>heart-o</title>
|
||||
<path d="M9.5 5c3.433 0 5.645 2.066 6.5 2.938 0.855-0.871 3.067-2.938 6.5-2.938 4.138 0 7.5 3.404 7.5 7.5 0 2.857-2.469 5.031-2.469 5.031l-11.531 11.563-0.719-0.719-10.813-10.844s-0.619-0.573-1.219-1.469-1.25-2.134-1.25-3.563c0-4.096 3.362-7.5 7.5-7.5zM9.5 7c-3.042 0-5.5 2.496-5.5 5.5 0 0.772 0.423 1.716 0.906 2.438s0.969 1.188 0.969 1.188l10.125 10.125 10.125-10.125s1.875-2.080 1.875-3.625c0-3.004-2.458-5.5-5.5-5.5-2.986 0-5.75 2.906-5.75 2.906l-0.75 0.844-0.75-0.844s-2.764-2.906-5.75-2.906z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 668 B |
@ -1,5 +1 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>location-arrow</title>
|
||||
<path d="M16 4.438l0.906 2.188 8 19 0.906 2.125-2.156-0.813-7.656-2.875-9.813 3.688 0.906-2.125 8-19zM16 9.531l-6.188 14.719 6.188-2.313 0.344 0.125 5.844 2.188z"></path>
|
||||
</svg>
|
||||
<!-- Generated by IcoMoon.io --><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><title>location-arrow</title><path d="M16 4.438l0.906 2.188 8 19 0.906 2.125-2.156-0.813-7.656-2.875-9.813 3.688 0.906-2.125 8-19zM16 9.531l-6.188 14.719 6.188-2.313 0.344 0.125 5.844 2.188z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 335 B |
@ -1,7 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
|
||||
<path d="M31.7,29.2L29.8,23V7.1c0-1.1-1-2.1-2.1-2.1H4.8c-1.1,0-2.1,1-2.1,2.1V23l-2,6.1c-0.2,0.6-0.1,1.3,0.2,1.9
|
||||
c0.4,0.6,1,0.9,1.7,0.9h27.1c0.7,0,1.3-0.3,1.7-0.9C31.8,30.4,31.9,29.7,31.7,29.2z M4.2,7.1c0-0.3,0.3-0.6,0.6-0.6h22.8
|
||||
c0.3,0,0.6,0.3,0.6,0.6v15.3h-24V7.1z M30.2,30.2c-0.1,0.1-0.2,0.2-0.5,0.2H2.6c-0.3,0-0.4-0.2-0.5-0.2C2.1,30,2,29.8,2.1,29.6
|
||||
L4,23.9h24.4l1.8,5.8C30.4,29.8,30.3,30,30.2,30.2z"/>
|
||||
<rect x="13.5" y="27.8" width="5.6" height="1.5"/>
|
||||
</svg>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32"><path d="M31.7,29.2L29.8,23V7.1c0-1.1-1-2.1-2.1-2.1H4.8c-1.1,0-2.1,1-2.1,2.1V23l-2,6.1c-0.2,0.6-0.1,1.3,0.2,1.9 c0.4,0.6,1,0.9,1.7,0.9h27.1c0.7,0,1.3-0.3,1.7-0.9C31.8,30.4,31.9,29.7,31.7,29.2z M4.2,7.1c0-0.3,0.3-0.6,0.6-0.6h22.8 c0.3,0,0.6,0.3,0.6,0.6v15.3h-24V7.1z M30.2,30.2c-0.1,0.1-0.2,0.2-0.5,0.2H2.6c-0.3,0-0.4-0.2-0.5-0.2C2.1,30,2,29.8,2.1,29.6 L4,23.9h24.4l1.8,5.8C30.4,29.8,30.3,30,30.2,30.2z"/><rect x="13.5" y="27.8" width="5.6" height="1.5"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 596 B After Width: | Height: | Size: 578 B |