@zavvla/shared-socials

v0.1.3

Published

Lightweight social sharing widget with analytics support

Readme

@zavvla/shared-socials — Виджет «Поделиться социальной сетью»

npm npm bundle size TypeScript Zero dependencies License: MIT

Русский | English


Лёгкий виджет шаринга для социальных сетей. Zero-dependency TypeScript библиотека.

  • Zero dependencies — чистый TypeScript, без сторонних зависимостей
  • Shadow DOM — изоляция стилей по умолчанию, CSS инжектируется автоматически
  • 20 встроенных сетей — VK, Telegram, WhatsApp, Twitter/X, Facebook и другие
  • Гибкая инициализация — через data-атрибуты или JS API
  • Аналитика — Яндекс.Метрика, Google Analytics 4, кастомные адаптеры
  • Кастомные сети — добавляйте любые соц. сети

Установка

npm install @zavvla/shared-socials

Быстрый старт

CDN (без сборщика)

<div data-share-widget data-networks="vk,telegram,whatsapp"></div>

<script src="https://unpkg.com/@zavvla/shared-socials/dist/shared-socials.global.js"></script>

CSS подключать не нужно — стили инжектируются автоматически через Shadow DOM.

ESM / TypeScript

import { ShareWidget } from '@zavvla/shared-socials'

new ShareWidget({
  target: '#share',
  networks: ['vk', 'telegram', 'whatsapp'],
  url: 'https://example.com',
  title: 'Заголовок страницы',
})

Data-атрибуты

| Атрибут | Тип | По умолчанию | Описание | |---------------------|---------------------------------|------------------|-----------------------------------| | data-share-widget | — | — | Маркер для авто-инициализации | | data-networks | vk,telegram,... | vk,telegram | Список сетей через запятую | | data-url | string | текущий URL | URL для шаринга | | data-title | string | document.title | Заголовок | | data-description | string | og:description | Описание | | data-image | string | og:image | URL изображения | | data-mode | icon | text | icon-text | icon-text | Режим отображения кнопок | | data-icon-mode | svg | css | svg | Способ рендера иконок | | data-open-mode | blank | popup | blank | Открывать в новой вкладке / popup | | data-shadow | boolean (наличие атрибута) | включён | Shadow DOM изоляция |

JS API

Конфигурация

new ShareWidget({
  target: '#share',             // string | HTMLElement, по умолчанию document.body
  networks: ['vk', 'telegram'], // список сетей (по умолчанию ['vk', 'telegram'])
  url: 'https://example.com',   // по умолчанию window.location.href
  title: 'Заголовок',           // по умолчанию document.title
  description: 'Описание',      // по умолчанию og:description meta
  image: 'https://example.com/og.jpg', // по умолчанию og:image meta

  mode: 'icon-text',            // 'icon' | 'text' | 'icon-text'
  iconMode: 'svg',              // 'svg' | 'css'
  openMode: 'blank',            // 'blank' | 'popup'
  popup: { width: 700, height: 520 },

  shadow: true,                 // Shadow DOM (по умолчанию: true)
  injectStyles: true,           // авто-инжект CSS (по умолчанию: true)

  classes: {
    container: 'my-share',
    button: 'my-share__btn',
    buttonNetworkPrefix: 'my-share__btn--',
    icon: 'my-share__icon',
    label: 'my-share__label',
  },

  metrics: {
    adapters: [new YandexMetrikaAdapter({ counterId: 12345 })],
    onEvent: (event) => console.log(event),
  },
})

Методы

widget.update({ mode: 'icon', networks: ['vk', 'telegram'] }) // обновить конфиг
widget.destroy()                                               // удалить из DOM

События

widget
  .on('click',  ({ network, url }) => console.log('клик', network))
  .on('open',   ({ network, url }) => console.log('открыто', network))
  .on('cancel', ({ network, url }) => console.log('popup закрыт'))
  .on('render', ({ url }) => console.log('виджет отрендерен'))
  .on('error',  ({ network, message }) => console.error(message))

| Событие | Payload | Когда | |----------|-----------------------------|----------------------------------------------| | click | { network, url } | Пользователь кликнул кнопку | | open | { network, url } | Окно/вкладка шаринга открылась | | cancel | { network, url } | Popup закрыт без шаринга (openMode: popup) | | render | { url } | Виджет смонтирован в DOM | | error | { network, url, message } | Ошибка при открытии окна |

Встроенные сети

| ID | Название | ID | Название | |--------------|---------------|--------------|-------------| | vk | ВКонтакте | linkedin | LinkedIn | | ok | Одноклассники | pinterest | Pinterest | | telegram | Telegram | reddit | Reddit | | whatsapp | WhatsApp | tumblr | Tumblr | | twitter | Twitter / X | skype | Skype | | viber | Viber | line | Line | | facebook | Facebook | bluesky | Bluesky | | hackernews | Hacker News | pocket | Pocket | | weibo | Weibo | xing | Xing | | mastodon | Mastodon | email | Email |

Shadow DOM и стили

// Shadow DOM + авто-стили (по умолчанию)
new ShareWidget({ target: '#share' })

// Shadow DOM, стили пишешь сам через ::part()
new ShareWidget({ target: '#share', injectStyles: false })

// Без Shadow DOM, авто-стили в <head>
new ShareWidget({ target: '#share', shadow: false, injectStyles: true })

// Без Shadow DOM, CSS вручную через <link>
new ShareWidget({ target: '#share', shadow: false, injectStyles: false })

Стилизация через CSS Parts API (при shadow: true)

#share::part(container)  { display: flex; gap: 8px; }
#share::part(button)     { border-radius: 8px; padding: 8px 12px; }
#share::part(button-vk)  { background: #0077ff; color: #fff; }
#share::part(icon)       { width: 20px; height: 20px; }
#share::part(label)      { font-size: 14px; }

CSS-классы по умолчанию (при shadow: false)

| Элемент | Класс | |-------------|---------------------------| | Контейнер | share-box | | Кнопка | share-box__button | | Кнопка сети | share-box__button--{id} | | Иконка | share-box__icon | | Текст | share-box__label |

Кастомные сети

import { ShareWidget } from '@zavvla/shared-socials'
import type { NetworkDefinition } from '@zavvla/shared-socials'

const livejournalNetwork: NetworkDefinition = {
  id: 'livejournal',
  label: 'LiveJournal',
  defaultIcon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">...</svg>`,
  buildUrl({ url, title }) {
    return `https://www.livejournal.com/update.bml?subject=${encodeURIComponent(title ?? '')}&prop_url=${encodeURIComponent(url)}`
  },
}

new ShareWidget({
  networks: ['vk', 'telegram', 'livejournal'],
  customNetworks: [livejournalNetwork],
})

Аналитика

Яндекс.Метрика

import { ShareWidget, YandexMetrikaAdapter } from '@zavvla/shared-socials'

new ShareWidget({
  metrics: {
    adapters: [new YandexMetrikaAdapter({ counterId: 12345678 })],
  },
})

Google Analytics 4

import { GoogleAnalyticsAdapter } from '@zavvla/shared-socials/analytics'

new ShareWidget({
  metrics: {
    adapters: [new GoogleAnalyticsAdapter({ measurementId: 'G-XXXXXXXXXX' })],
  },
})

Кастомный адаптер

import type { MetricsAdapter, ShareMetricEvent } from '@zavvla/shared-socials'

class MyAdapter implements MetricsAdapter {
  readonly name = 'MyAnalytics'
  readonly docsUrl = 'https://my-analytics.example.com'

  isAvailable() {
    return typeof window.myAnalytics !== 'undefined'
  }

  track(event: ShareMetricEvent) {
    window.myAnalytics.send(event.type, { network: event.network, url: event.url })
  }
}

new ShareWidget({ metrics: { adapters: [new MyAdapter()] } })

Лицензия

MIT © zavvla


English

Lightweight social sharing widget. Zero-dependency TypeScript library.

  • Zero dependencies — pure TypeScript, no third-party dependencies
  • Shadow DOM — style isolation by default, CSS injected automatically
  • 20 built-in networks — VK, Telegram, WhatsApp, Twitter/X, Facebook and more
  • Flexible initialization — via data attributes or JS API
  • Analytics — Yandex.Metrika, Google Analytics 4, custom adapters
  • Custom networks — add any social network

Installation

npm install @zavvla/shared-socials

Quick Start

CDN (no bundler)

<div data-share-widget data-networks="vk,telegram,whatsapp"></div>

<script src="https://unpkg.com/@zavvla/shared-socials/dist/shared-socials.global.js"></script>

No need to include CSS — styles are injected automatically via Shadow DOM.

ESM / TypeScript

import { ShareWidget } from '@zavvla/shared-socials'

new ShareWidget({
  target: '#share',
  networks: ['vk', 'telegram', 'whatsapp'],
  url: 'https://example.com',
  title: 'Page title',
})

Data Attributes

| Attribute | Type | Default | Description | |---------------------|---------------------------------|------------------|-----------------------------------| | data-share-widget | — | — | Marker for auto-initialization | | data-networks | vk,telegram,... | vk,telegram | Comma-separated network list | | data-url | string | current URL | URL to share | | data-title | string | document.title | Title | | data-description | string | og:description | Description | | data-image | string | og:image | Image URL | | data-mode | icon | text | icon-text | icon-text | Button display mode | | data-icon-mode | svg | css | svg | Icon render method | | data-open-mode | blank | popup | blank | Open in new tab or popup | | data-shadow | boolean (attribute presence) | enabled | Shadow DOM isolation |

JS API

Configuration

new ShareWidget({
  target: '#share',             // string | HTMLElement, default: document.body
  networks: ['vk', 'telegram'], // network list (default: ['vk', 'telegram'])
  url: 'https://example.com',   // default: window.location.href
  title: 'Title',               // default: document.title
  description: 'Description',   // default: og:description meta
  image: 'https://example.com/og.jpg', // default: og:image meta

  mode: 'icon-text',            // 'icon' | 'text' | 'icon-text'
  iconMode: 'svg',              // 'svg' | 'css'
  openMode: 'blank',            // 'blank' | 'popup'
  popup: { width: 700, height: 520 },

  shadow: true,                 // Shadow DOM (default: true)
  injectStyles: true,           // auto-inject CSS (default: true)

  classes: {
    container: 'my-share',
    button: 'my-share__btn',
    buttonNetworkPrefix: 'my-share__btn--',
    icon: 'my-share__icon',
    label: 'my-share__label',
  },

  metrics: {
    adapters: [new YandexMetrikaAdapter({ counterId: 12345 })],
    onEvent: (event) => console.log(event),
  },
})

Methods

widget.update({ mode: 'icon', networks: ['vk', 'telegram'] }) // update config
widget.destroy()                                               // remove from DOM

Events

widget
  .on('click',  ({ network, url }) => console.log('clicked', network))
  .on('open',   ({ network, url }) => console.log('opened', network))
  .on('cancel', ({ network, url }) => console.log('popup closed'))
  .on('render', ({ url }) => console.log('widget rendered'))
  .on('error',  ({ network, message }) => console.error(message))

| Event | Payload | When | |----------|-----------------------------|----------------------------------------------| | click | { network, url } | User clicked a button | | open | { network, url } | Share window/tab opened | | cancel | { network, url } | Popup closed without sharing (openMode: popup) | | render | { url } | Widget mounted to DOM | | error | { network, url, message } | Error opening window |

Built-in Networks

| ID | Name | ID | Name | |--------------|---------------|--------------|-------------| | vk | VKontakte | linkedin | LinkedIn | | ok | Odnoklassniki | pinterest | Pinterest | | telegram | Telegram | reddit | Reddit | | whatsapp | WhatsApp | tumblr | Tumblr | | twitter | Twitter / X | skype | Skype | | viber | Viber | line | Line | | facebook | Facebook | bluesky | Bluesky | | hackernews | Hacker News | pocket | Pocket | | weibo | Weibo | xing | Xing | | mastodon | Mastodon | email | Email |

Shadow DOM & Styles

// Shadow DOM + auto styles (default)
new ShareWidget({ target: '#share' })

// Shadow DOM, custom styles via ::part()
new ShareWidget({ target: '#share', injectStyles: false })

// No Shadow DOM, auto styles in <head>
new ShareWidget({ target: '#share', shadow: false, injectStyles: true })

// No Shadow DOM, manual CSS via <link>
new ShareWidget({ target: '#share', shadow: false, injectStyles: false })

CSS Parts API (when shadow: true)

#share::part(container)  { display: flex; gap: 8px; }
#share::part(button)     { border-radius: 8px; padding: 8px 12px; }
#share::part(button-vk)  { background: #0077ff; color: #fff; }
#share::part(icon)       { width: 20px; height: 20px; }
#share::part(label)      { font-size: 14px; }

Default CSS Classes (when shadow: false)

| Element | Class | |-----------|---------------------------| | Container | share-box | | Button | share-box__button | | Network | share-box__button--{id} | | Icon | share-box__icon | | Label | share-box__label |

Custom Networks

import { ShareWidget } from '@zavvla/shared-socials'
import type { NetworkDefinition } from '@zavvla/shared-socials'

const livejournalNetwork: NetworkDefinition = {
  id: 'livejournal',
  label: 'LiveJournal',
  defaultIcon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">...</svg>`,
  buildUrl({ url, title }) {
    return `https://www.livejournal.com/update.bml?subject=${encodeURIComponent(title ?? '')}&prop_url=${encodeURIComponent(url)}`
  },
}

new ShareWidget({
  networks: ['vk', 'telegram', 'livejournal'],
  customNetworks: [livejournalNetwork],
})

Analytics

Yandex.Metrika

import { ShareWidget, YandexMetrikaAdapter } from '@zavvla/shared-socials'

new ShareWidget({
  metrics: {
    adapters: [new YandexMetrikaAdapter({ counterId: 12345678 })],
  },
})

Google Analytics 4

import { GoogleAnalyticsAdapter } from '@zavvla/shared-socials/analytics'

new ShareWidget({
  metrics: {
    adapters: [new GoogleAnalyticsAdapter({ measurementId: 'G-XXXXXXXXXX' })],
  },
})

Custom Adapter

import type { MetricsAdapter, ShareMetricEvent } from '@zavvla/shared-socials'

class MyAdapter implements MetricsAdapter {
  readonly name = 'MyAnalytics'
  readonly docsUrl = 'https://my-analytics.example.com'

  isAvailable() {
    return typeof window.myAnalytics !== 'undefined'
  }

  track(event: ShareMetricEvent) {
    window.myAnalytics.send(event.type, { network: event.network, url: event.url })
  }
}

new ShareWidget({ metrics: { adapters: [new MyAdapter()] } })

License

MIT © zavvla