Frontend Den – Telegram
Frontend Den
1.35K subscribers
73 photos
11 videos
24 files
277 links
Все дял починаючих веб-розробників і не тільки. Зв'язок з адміном або запропонувати новину 👉 @denyspopov_web
@junnot_chat
#frontend #wordpress #html #css
Download Telegram
Чистий круговий текст CSS (без використання моноширинних шрифтів) - https://frontendmasters.com/blog/pure-css-circular-text-without-requiring-a-monospace-font/
👍3
Привіт, 19 жовтня відбудеться React+ fwdays'24 — щорічна конференція для всіх, хто цікавиться розробкою на JavaScript 🤩

Основний фокус заходу — React. Проте, цього разу, Fwdays не обмежуються лише React та поговорять і про інші фреймворки, такі як Vue.js, Node.js, Angular, Svelte та інші.

Вас очікують:
📍Цікаві спікери та практичні доповіді про покращення React за допомогою TypeScript, мікро-фронтенд та монорепо, здоровий ґлузд та перфекціонізм під час рефакторингу та багато іншого.
📍Нетворкінг, Q&A зі спікерами, нові знайомства
📍Вайб Halloween під час офлайн частини заходу 👻
📍Розіграші та подарунки від партнерів

Формат: онлайн та офлайн у Києві

Використайте промокод POPOVREACT10 та отримайте знижку 10%, деталі за посиланням 👉 https://bit.ly/3TvYp6u

Приєднуйтесь до React+ fwdays'24!
5👍1
Коротше, вирішив я проаналізувати архітектуру проєкту.
Часу, щоб самому її малювати, немає, та й лінь. Вирішив трохи пошукати інформацію, бо думаю, що таких, як я, багато, і рішення точно вже якесь є.
Коротше, по факту, все вийшло.
Результат на фото.

Він не ідеальний, але якщо трохи підкрутити, змінити порядок блоків і прибрати дублікати (так вийшло), то все буде взагалі дуже непогано.

Нижче декілька дописів із повним кодом і описом, що й як.
👍4🔥3
Частина 1

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { parse } from "@vue/compiler-sfc";
import { load } from "cheerio";
import glob from "glob";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);
const globAsync = promisify(glob);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const srcDir = path.join(__dirname, "src");

const componentsDTSPath = path.join("", "components.d.ts"); // Change the path if needed

const groupColors = ["red", "blue", "green", "orange", "purple", "yellow", "pink", "cyan"];

const sharedComponents = ["SVGIcons", "Button", "Input", "Tooltip", "Calendar", "ValidationErrorBlock"];

onst getGlobalComponents = (filePath) => {
const content = fs.readFileSync(filePath, "utf-8");
const globalComponents = [];
const regex = /(\w+): typeof import\(['"][^'"]+['"]\)\['default'\]/g;
let match;
while ((match = regex.exec(content)) !== null) {
globalComponents.push(match[1]);
}
return globalComponents;
};

const getUsedComponents = (templateContent, globalComponents) => {
const usedComponents = new Set();
const $ = load(templateContent, { xmlMode: true, decodeEntities: false });

const toKebabCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();

$("*").each((_, elem) => {
const tagName = elem.tagName;
const possibleNames = [tagName, toKebabCase(tagName)];
possibleNames.forEach((name) => {
if (globalComponents.includes(name) || globalComponents.includes(toPascalCase(name))) {
usedComponents.add(name);
}
});
});

return Array.from(usedComponents).filter((comp) => !sharedComponents.includes(comp));
};

const toPascalCase = (str) =>
str
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join("");

const getComponentGroup = (relativePath) => {
const parts = relativePath.split(path.sep);
return parts.length > 1 ? parts[0] : "Others";
};
Частина 2

const generateArchitecture = async () => {
try {
if (!fs.existsSync(componentsDTSPath)) {
console.error(`File ${componentsDTSPath} not found.`);
return;
}

const globalComponents = getGlobalComponents(componentsDTSPath);
console.log("Globally registered components:", globalComponents);

const pattern = path.join(srcDir, "**/*.vue");
const vueFiles = await globAsync(pattern, { ignore: "**/node_modules/**" });

if (vueFiles.length === 0) {
console.warn("No Vue components found. Check the file path.");
return;
}

console.log(`Found ${vueFiles.length} Vue components.`);

const dependencyGraph = {};
const groups = {};

vueFiles.forEach((file) => {
const relativePath = path.relative(srcDir, file);
const componentName = path.basename(relativePath, ".vue");
dependencyGraph[componentName] = [];
const group = getComponentGroup(relativePath);
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(componentName);
});

for (const file of vueFiles) {
const relativePath = path.relative(srcDir, file);
const componentName = path.basename(relativePath, ".vue");

const content = fs.readFileSync(file, "utf-8");
const { denoscriptor } = parse(content);

if (!denoscriptor.template) {
console.warn(`File ${file} does not contain a <template>.`);
continue;
}

const templateContent = denoscriptor.template.content;
const usedComponents = getUsedComponents(templateContent, globalComponents);

dependencyGraph[componentName] = usedComponents;
console.log(`Component: ${componentName}, Uses: ${usedComponents.join(", ")}`);
}

let dotContent = 'digraph G {\n node [shape=box];\n rankdir=LR;\n splines=true;\n';

Object.keys(groups).forEach((group, index) => {
const color = groupColors[index % groupColors.length];
dotContent += ` subgraph cluster_${index} {\n label="${group}";\n color="${color}";\n`;
groups[group].forEach((component) => {
dotContent += ` "${component}";\n`;
});
dotContent += " }\n";
});

Object.keys(dependencyGraph).forEach((component) => {
const dependencies = dependencyGraph[component];
if (dependencies.length > 0) {
dependencies.forEach((dep) => {
dotContent += ` "${component}" -> "${dep}";\n`;
});
}
});

dotContent += "}\n";

const dotDir = path.join(__dirname, "docs", "architecture");
const dotPath = path.join(dotDir, "architecture.dot");
fs.mkdirSync(dotDir, { recursive: true });
fs.writeFileSync(dotPath, dotContent, "utf-8");

console.log("\nFile architecture.dot successfully created.");

const outputPngPath = path.join(dotDir, "architecture.png");
const cmd = `dot -Tpng "${dotPath}" -o "${outputPngPath}"`;
const { stdout, stderr } = await execAsync(cmd);
if (stderr) {
console.error(`Graphviz stderr: ${stderr}`);
}

console.log(`Architecture diagram generated at: ${outputPngPath}`);
} catch (error) {
console.error(`Error generating architecture: ${error.message}`);
}
};

generateArchitecture();
Для роботи скрипта необхідно встановити наступні залежності:

npm install @vue/compiler-sfc cheerio glob js-cookie util


@vue/compiler-sfc — для парсингу .vue файлів
cheerio — для роботи з HTML (розбір шаблонів Vue)
glob — для пошуку файлів у файловій системі
js-cookie — для роботи з кукі (якщо використовується у застосунку)
util — для промисифікації функцій.

Для генерації графу потрібен Graphviz

На macOS

brew install graphviz


На Ubuntu/Debian/Windows треба пошукати 😅

У мене весь проект лежить в src
А ще у мене є components.d.ts для автоматичного підкючення компонентів

По суті той скрипт що вище сканує всі .vue файли в папці src, потім визначає залежності між компонентами. Створює файл architecture.dot, який використовується для генерації графу.

Приклад результату
Якщо структура вашого проєкту виглядає так:

src/
├── components/
│ ├── Button.vue
│ ├── Modal.vue
├── pages/
│ ├── Home.vue
│ ├── Dashboard.vue
├── shared/
│ ├── Header.vue
│ ├── Footer.vue


То граф архітектури виглядатиме приблизно так:

Групи:
components: Modal, Button.
pages: Home, Dashboard.
shared: Header, Footer.

Залежності:
Home → Header.
Dashboard → Header, Footer.
Modal → Button.

Короче у підсумку виходить дуже не поганий інструмент для візуалізації архітектури

Він по суті може допогти
1. Швидко розуміти залежності між компонентами.
2. Визначати зайві зв'язки.
3. Покращувати модульність застосунку.
🔥132🤝1
12 бібліотек, які вам можуть стати у нагоді 😉

AOS (Animate on Scroll) — це легка та налаштовувана бібліотека для додавання ефектних анімацій при прокручуванні ваших вебсторінок.
https://michalsnik.github.io/aos

Chart.js - дозволяє легко створювати різні типи графіків, включаючи лінійні, стовпчасті, радарні та кругові, з мінімальною конфігурацією.
https://www.chartjs.org

SweetAlert2 пропонує адаптивні, повністю налаштовувані спливаючі вікна, які легко інтегруються у ваші проєкти.
https://sweetalert2.github.io

SortableJS дозволяє легко реалізувати функцію перетягування для списків. Її гнучкість робить її ідеальною для створення інтерактивних інтерфейсів, таких як канбан-дошки.
https://sortablejs.github.io/Sortable

Floating UI дозволяє легко керувати складними компонентами інтерфейсу, такими як підказки, випадаючі меню та спливаючі вікна.
https://floating-ui.com

FullCalendar пропонує зручний, повнофункціональний інтерфейс календаря з налаштовуваним керуванням подіями, функцією перетягування та різними режимами перегляду.
https://fullcalendar.io

Animate.css - додавайте попередньо створені анімації до елементів вашого сайту за допомогою Animate.css.
https://animate.style

Lottie від Airbnb - Ця бібліотека дозволяє інтегрувати високоякісні анімації у ваші вебдодатки.
https://lottiefiles.com/free-animations/airbnb

Tippy.js - Легка, розширювана бібліотека для створення красивих, налаштовуваних підказок, спливаючих вікон і випадаючих меню.
https://atomiks.github.io/tippyjs

Day.js — це проста й швидка бібліотека, яка чудово підходить для проєктів, що працюють з датами, не займаючи багато місця та не уповільнюючи роботу.
https://day.js.org

Swiper — це безкоштовний і потужний інструмент для створення слайдерів і каруселей. Він забезпечує плавні переходи й добре працює на різних екранах, що робить його ідеальним для мобільних проєктів.
https://swiperjs.com

Vivus - Створюйте приголомшливі анімовані SVG-малюнки за допомогою Vivus. Ця легка JavaScript-бібліотека дозволяє анімувати SVG, імітуючи процес малювання, додаючи унікальний стиль вашим графікам.
https://maxwellito.github.io/vivus
🔥192👍1
Channel name was changed to «Frontend Den»
26 листопада був реліз Vite 6
Про нові зміни можна почитати тут
Якщо коротко, то:
- Деякі умови більше не додаються автоматично і мають бути вказані в конфігурації.
- JSON.stringify тепер за замовчуванням працює в режимі 'auto'
- За замовчуванням використовується сучасний API.
- У бібліотечному режимі ім'я CSS-файлу тепер залежить від назви пакету.
👍3
Якщо ви цікавитесь Vue як і я, ось вам цікавенька стаття де розповідають як побудувати веб апплікейшин який видаляє фон за допомогою Vue та Transformers.js
👍11🔥2
Зробив із ноутбука власний сервер.

Хотів я арендувати VPS для себе щоб на ньому розмістити свої проекти. Довго шукав, читав, питав і так і не зміг обрати найкращий. А потім я згадав що у мене є ноутбук, який лежить без діла. Тому склавши такі речі як: ноутбук без діла + бажання розібратись як підняті свій сервер, я почав його підіймати

1️⃣ Спочатку встановив Ubuntu Server ( останню версію Ubuntu Server можна завантажити з офіційного сайту )

При встановленні там все інтуітивно зрозуміло.
Єдино що: я обрав мінімальну конфігурацію, одразу під час встановлення додав OpenSSH Server.

2️⃣ Далі після встановлення я новоспечений сервер залишив на столі і пішов вже сів за свій ноут. До серверу підключивя через SSH через локальну мережу.

Як це зробити
Дізнайтеся локальну IP-адресу сервера
ip a

Приклад виводу: 192.168.1.100 — ваш локальний IP.

Щоб підключитися
ssh username@192.168.1.100


Далі вас попросе пароль.
Введіть його. Ну і якщо правльно ввели, то все, ви зайшли на свій сервер

3️⃣ Далі налаштування фаєрволу (UFW)

Налаштуйте захист сервера, дозволивши лише потрібні порти.

Встановіть UFW:
sudo apt update
sudo apt install ufw


Дозвольте доступ для SSH та веб-сервера:
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 22


Увімкніть фаєрвол:
sudo ufw enable


Перевірка статусу
sudo ufw status


4️⃣ Встановлення PHP та MySQL

Встановіть PHP та необхідні модулі:
sudo apt install php php-fpm php-mysql


Встановіть MySQL
sudo apt install mysql-server


Налаштуйте MySQL для безпечної роботи (слідуйте інструкціям):
sudo mysql_secure_installation


sudo ufw status


5️⃣ Встановлення веб-сервера Nginx

sudo apt install nginx


У браузері на іншому пристрої введіть локальну IP-адресу сервера, наприклад http://192.168.1.100

6️⃣ Налаштування Nginx для роботи з PHP

sudo nano /etc/nginx/sites-available/example


Сам конфіг такий
server {
listen 80;
server_name <ваш_білий_IP>;

root /var/www/html;
index index.php index.html index.htm;

location / {
try_files $uri $uri/ =404;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_noscript_name;
include fastcgi_params;
}

location ~ /\.ht {
deny all;
}
}


Створюєм симлінк для активації конфігурації:
sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/


Рестартім nginx
sudo nginx -t
sudo systemctl reload nginx


7️⃣ Потім щоб все працювало рівно мені довелось купити у свого провайдера білий айпі. Це коштує 30 грн/місяць. У вашого провайдера це може бути взашалі безкоштовно

І коли у вас є білий айпи, і якщо ви пидєднуєте ваш сервер до мережі інтернет через роутер, треба налаштувати проброс портів на ньому ( роутері ).

У мене в налаштуваннях роутера є така вкладка як Виртуальный сервер
і там вказую зовнішній порт ( 80 ), внутрішній порт ( 80 ), локальну айпі адресу ( 192.168.1.100 ) і протокол ( TCP )

і потім треба зробити ще одну таку запис для SSH
овнішній порт ( 20 ), внутрішній порт ( 20 ), локальну айпі адресу ( 192.168.1.100 ) і протокол ( ALL )

Сам зовнішній айпі можна дізнатися так
curl ifconfig.me


8️⃣ Розміщення файлів і все таке відбувається тут /var/www/html

Ту сторінку що ви бачите коли перйший раз заходите можете знайти тут
sudo nano /var/www/html/index.html


Це поки все що я зробив 🙂

Далі хочу розгорнути сайт на Wordpress
Купити якийсь дешевий домен і привязати його
А ще, Налаштування HTTPS

Поки все 🙃
🔥18👍84
#CSS шпаргалка яка може стати у нагоді, особливо тим хто тільки починає

#frontend #html #розробка #фронтенд
16
Відносно нові речі, які ви повинні знати про HTML у 2025 року - https://frontendmasters.com/blog/bone-up-html-2025/
👍14
https://una.im/advanced-attr/ - Нові можливості attr()

Цікава стаття.
Точно вартої вашої уваги бо впевнений що рано чи пізно у вас буде кейс де attr() вас спасе 😉


https://css-tricks.com/positioning-text-around-elements-with-css-offset/ - Розташування тексту навколо елементів із зсувом CSS. Виглядає дуже гарно

https://css-tricks.com/some-things-you-might-not-know-about-custom-counter-styles/ - Деякі речі, які ви можете не знати про спеціальні стилі лічильників
👍5
Всім првиіт
Стартувало друге голосування на Премію Доу
Я увійшов у шорт ліст серед кандитатів в категоріі "Вони – надихають"
Тому, якщо у вас є трохи хвилинок, зайдіть сюди https://dou.ua/awards-2025/ і залиште будь ласка свій голос 😉
🔥10
😍 JavaScript fwdays’25 – головна подія року для JS-розробників!

Хочете бути в курсі останніх трендів у JavaScript, почути топових спікерів та познайомитися з однодумцями?

🗓 Дата: 24 травня 2025
📍 Де: Онлайн + Київ (офлайн)
🎤 Спікери: ТОП JS-гуру, яких ви точно знаєте

На вас чекають реальні кейси, новітні технології та багато коду!

Використайте промокод POPOVJS10 та отримайте знижку 10%, деталі за посиланням 👉 https://bit.ly/43lx7FL
6