• Как разбить многостроковый текст на массив с помощью регулярного выражения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Понимаю что можно сделать проще с помощью split, но хочу научиться именно с помощью регулярного выражения

    Одно другому не мешает:

    str.split(/\n(?=#EXTINF)/)
    Ответ написан
    Комментировать
  • Как кликать на вложенные элементы так, чтобы не кликался родительский элемент?

    0xD34F
    @0xD34F Куратор тега JavaScript
    .dropdown.show {
      display: block;
    }

    const accordionItemSelector = '.data-accordion--summary-container';
    const contentSelector = '.dropdown';
    const activeClass = 'show';
    
    document.body.addEventListener('click', ({ target: t }) => {
      if (t.matches(accordionItemSelector)) {
        document.querySelectorAll(contentSelector).forEach(n => {
          if (!n.contains(t) && !t.contains(n)) {
            n.classList.remove(activeClass);
          };
        });
    
        t.querySelector(contentSelector).classList.toggle(activeClass);
      }
    });
    Ответ написан
    Комментировать
  • Как из query string получить объект следующего вида?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Просто исправить существующий код:

    - acc[key] = [value];
    + (acc[key] ??= []).push(value);

    Но можно его ещё и сократить:

    const convertQueryStringToObject = str => Array
      .from(new URLSearchParams(str))
      .reduce((acc, n) => ((acc[n[0]] ??= []).push(n[1]), acc), {});

    Или неоправданно усложнить и изуродовать:

    const convertQueryStringToObject = str => Array
      .from(str.matchAll(/([^&]+)=([^&]+)/g))
      .reduce((acc, [ , k, v ]) => (
        Object.hasOwn(acc, k) || (acc[k] = []),
        acc[k][acc[k].length] = v,
        acc
      ), {});
    Ответ написан
    1 комментарий
  • Как убрать scroll у body при открытии модальных окон через vue watch?

    0xD34F
    @0xD34F Куратор тега Vue.js
    .no-overflow {
      overflow: hidden;
    }

    mounted() {
      this.$watch(
        () => this.isModalAddVisible || this.activeId,
        val => document.body.classList.toggle('no-overflow', val),
        { immediate: true }
      );
    },

    Одно непонятно - зачем отслеживать несколько разных свойств, если наличием скролла может управлять само модальное окно? Открылось - убирает скролл, закрылось - возвращает. Разные окна для разных задача? - сделайте общий компонент, а разницу для конкретных экземпляров определяйте через слоты.
    Ответ написан
    Комментировать
  • Как переместить элемент в блоке вверх по клику?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Данные слайдов сложить в массив. Сделать вычисляемое свойство - данные всех слайдов кроме верхнего. Создать верхний слайд, создать остальные слайды на основе вычисляемого свойства. Если нужна анимация перемещения, завернуть слайды в transition-group. Вот так всё просто.
    Ответ написан
    1 комментарий
  • Как динамически добавлять текстовые поля в форму редактирования строки таблицы с использованием Quasar?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Сложить имена основных полей в массив.

    Сделать вычисляемое свойство, представляющее имена дополнительных полей - надо получить список ключей editedItem и выкинуть из него имена основных полей.

    В диалоговом окне сделать v-for по массиву дополнительных полей.

    Всё.
    Ответ написан
    Комментировать
  • Как реализовать эффект печатающегося текста?

    0xD34F
    @0xD34F Куратор тега React
    Состояние компонента должно содержать три значения - индекс печатаемой строки, количество отображаемых символов, направление печати (печатаем или стираем, т.е., в какую сторону надо изменять количество отображаемых символов, увеличивать или уменьшать). Дальше понятно что - setInterval или рекурсивный setTimeout, изменяем количество отображаемых символов, если дошли до конца строки - меняем направление, дошли до начала - меняем направление и индекс строки. Вот как-то так:

    const defaults = {
      index: 0,
      length: 0,
      step: 1,
    };
    
    function Typewriter({ strings, delay }) {
      const [ state, setState ] = useState(null);
    
      useEffect(() => {
        setState(() => ({...defaults}));
      }, [ strings ]);
    
      useEffect(() => {
        const timeoutId = setTimeout(setState, delay, ({...state}) => {
          state.length += state.step;
          if (state.length === strings[state.index].length) {
            state.step = -1;
          } else if (state.length === 0) {
            state.step = 1;
            state.index = (state.index + 1) % strings.length;
          }
          return state;
        });
    
        return () => clearTimeout(timeoutId);
      });
    
      return <div>{strings?.[state?.index]?.slice(0, state?.length)}</div>;
    }
    Ответ написан
    8 комментариев
  • Как высчитать уровень исходя из массива?

    0xD34F
    @0xD34F Куратор тега JavaScript
    работает, но кажется, что достаточно криво

    Да, криво - всегда перебирается весь массив.

    Можно идти от конца к началу до тех пор, пока не встретится подходящий элемент:

    let lvl = 0;
    
    for (let i = LVLS.length; i--;) {
      if (LVLS[i].exp <= EXP) {
        lvl = LVLS[i].lv;
        break;
      }
    }

    переделать это во что-то более красивое

    const lvl = LVLS.findLast(n => n.exp <= EXP)?.lv ?? 0;

    А вообще, учитывая, что уровни представлены натуральными числами без пропусков, отдельные свойства под их значения не нужны, можно искать индекс:

    const LVLS = [ 10, 25, 45, 70, 100 ];
    
    const lvl = 1 + LVLS.findLastIndex(n => n <= EXP);
    Ответ написан
    Комментировать
  • Как получить свойство из props drilling по открытию модального окна?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Не надо никаких v-model, кого показывать в модальном окне - отправляйте наверх эту информацию вместе с событием. Свойство, управляющее видимостью модального окна - пусть оно вместо логического значения хранит id или объект или что там у вас должно показываться в окне, если не null, значит открываем окно. Вот так всё просто.

    UPD. Вот вариант с v-model, конечно только на уровне таблицы, в строках всё по-прежнему.

    UPD. А вообще, можно полностью отказаться от прокидывания событий наверх. Таблицу исполняем в более общем виде - пусть принимает массив с описанием столбцов, а содержимое ячеек задаётся через слоты, имена которых определяются на основе имён столбцов (по умолчанию выводят данных как есть). В этом случае кнопку можно определить там же, где создаётся экземпляр таблицы. Как это может выглядеть - ваш вариант с отдельным компонентом строки (и, соответственно, прокидыванием в него слотов из компонента таблицы), или, без ненужного усложнения.
    Ответ написан
    1 комментарий
  • Как отобразить данные по тегам из json на странице?

    0xD34F
    @0xD34F Куратор тега JavaScript
    <button data-country="">Показать всё</button>
    <button data-country="France">Франция</button>
    <button data-country="Germany">Германия</button>
    <button data-country="England">Англия</button>

    const catalogEl = document.querySelector('#catalog-box');
    let catalogArr = [];
    
    const buttons = document.querySelectorAll('button[data-country]');
    buttons.forEach(n => n.addEventListener('click', onClick));
    
    function onClick({ target: t }) {
      buttons.forEach(n => n.classList.toggle('active', n === t));
      renderCatalog(t.dataset.country);
    }
    
    function renderCatalog(country) {
      const toRender = country
        ? catalogArr.filter(n => n.tag === country)
        : catalogArr;
    
      catalogEl.innerHTML = toRender
        .map(n => `
          <div class="catalog__card" id="${n.id}">
            <img src="${n.img}" alt="${n.alt}" class="catalog__card_img">
            <p class="catalog__card_author">${n.author}</p>
            <p class="catalog__card_name">${n.name}</p>
            <p class="catalog__card_note">${n.note}</p>
            <p class="catalog__card_price">${n.price}</p>
            <button class="catalog__card_btn">${n.btn}</button>
          </div>`)
        .join('');
    }
    
    fetch('catalogBox.json')
      .then(r => r.json())
      .then(r => {
        catalogArr = r;
        buttons[0].click();
      });
    Ответ написан
    Комментировать
  • Добавил элементы в массив в local storage, при обновлении страницы компоненты пропадают с экрана, но остаются в local storage, как это исправить?

    0xD34F
    @0xD34F Куратор тега Vue.js
    const addCard = (cardName) => {
      cards.value.push({
        id: Date.now(),
        component: cardName,
        order: cards.value.length + 1,
        isRequired: false
      })
    }

    Что такое cardName? Не то, что вам, судя по имени параметра, кажется. Это не имя, а сам объект компонента. И вы его потом в localStorage засовываете. Так что методы setup и render теряются.

    Сложите компоненты в объект:

    const components = {
      ShortTextCard,
      LongTextCard,
      SingleQuestionCard,
      MultiQuestionCard,
    };

    Передавайте в addCard их имена:

    - @click="addCard(ShortTextCard)"
    + @click="addCard('ShortTextCard')"

    Когда надо экземпляр компонента создать, доставайте объект компонента по его имени из упомянутого выше объекта:

    - :is="card.component"
    + :is="components[card.component]"
    Ответ написан
    1 комментарий
  • Как совместить переход по вкладкам из element plus и vue-router?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Лучше никак.

    Так что сперва о том, как надо. Вместо вкладок используем меню - оно умеет работать с роутером:

    <el-menu mode="horizontal" router :default-active="$route.name">
      <el-menu-item
        v-for="n in $router.getRoutes()"
        v-text="n.name"
        :index="n.name"
        :route="n"
      />
    </el-menu>
    <router-view />

    Ну а вкладки... Делаем вычисляемое свойство, представляющее активный маршрут, геттер - возвращает имя, сеттер - по имени выполняет переход:

    computed: {
      activeRouteName: {
        get() {
          return this.$route.name;
        },
        set(name) {
          this.$router.push({ name });
        },
      },
    },

    Используем это свойство для управления вкладками. Контент у всех вкладок одинаковый - <router-view>, но рендерить будем его только в активной вкладке. Вот такой получается говнокод:

    <el-tabs v-model="activeRouteName">
      <el-tab-pane v-for="{ name: n } in $router.getRoutes()" :label="n" :name="n">
        <router-view v-if="activeRouteName === n" />
      </el-tab-pane>
    </el-tabs>
    Ответ написан
    Комментировать
  • Почему правильно работающий код не проходит тест?

    0xD34F
    @0xD34F
    Откуда вы взяли, что функция должна что-то возвращать? Судя по названию - не должна. Вместо собирания и возврата строки сразу печатайте отдельные результаты. Ну или оставьте собирание строки и вместо return сделайте print.
    Ответ написан
    1 комментарий
  • Как в el-table вставить нужный мне id, для перехода на страницы?

    0xD34F
    @0xD34F Куратор тега Vue.js
    handleEdit(row){

    @click="handleEdit(scope.$index, scope.row)"

    Сколько параметров получает метод handleEdit? А сколько передаёте ему в обработчике клика? Устраните это печальное несоответствие.

    path:'/protocol_information/:id/edit/',
    params:{
        id:row.id
    }

    Вместо path должен быть name. Конечно, если таковой указан при определении маршрута.

    Или, вырезаете params и вставляете нужный id сразу в строку:

    path: `/protocol_information/${row.id}/edit/`,
    Ответ написан
  • Как сформировать массив объектов исходя из 2-х других массивов объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Сначала из первого массива сделаем объект, где значениями будут его элементы, а ключами - значения их, элементов, свойства name:

    const obj1 = Object.fromEntries(arr1.map(n => [ n.name, n ]));
    
    // или
    
    const obj1 = arr1.reduce((acc, n) => (acc[n.name] = n, acc), {});

    Затем можно собрать новый массив:

    const newArr2 = arr2.map(n => ({ ...obj1[n.name], ...n }));
    
    // или
    
    const newArr2 = [];
    for (let i = 0; i < arr2.length; i++) {
      newArr2.push(Object.assign({}, obj1[arr2[i].name], arr2[i]));
    }

    Или обновить существующий:

    arr2.forEach(n => Object
      .entries(obj1[n.name] ?? {})
      .forEach(([ k, v ]) => Object.hasOwn(n, k) || (n[k] = v))
    );
    
    // или
    
    for (const n of arr2) {
      const obj = obj1[n.name];
      for (const k in obj) {
        if (!n.hasOwnProperty(k)) {
          n[k] = obj[k];
        }
      }
    }

    UPD. Вынесено из комментариев:

    можно ли исключить объекты у которых не изменился value? То есть что бы этих объектов не было в итоговом массиве.

    const newArr2 = arr2.reduce((acc, n) => (
      obj1[n.name]?.value !== n.value && acc.push({ ...obj1[n.name], ...n }),
      acc
    ), []);
    Ответ написан
    5 комментариев
  • Как ограничить количество отмеченных чекбоксов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Отключаем чекбоксы, изменение состояния которых нежелательно:

    function restrictChecked({
      container,
      selector = 'input[type="checkbox"]',
      min = 0,
      max = Infinity,
      enableOnCancel = true,
    }) {
      const checkboxes = [...container.querySelectorAll(selector)];
      const onChange = () => {
        const countChecked = checkboxes.reduce((acc, n) => acc + n.checked, 0);
        const minReached = countChecked <= min;
        const maxReached = countChecked >= max;
        checkboxes.forEach(n => n.disabled = minReached && n.checked || maxReached && !n.checked);
      };
    
      checkboxes.forEach(n => n.addEventListener('change', onChange));
      onChange();
    
      return () => checkboxes.forEach(n => {
        n.disabled &&= !enableOnCancel;
        n.removeEventListener('change', onChange);
      });
    }

    Или, откатываем нежелательные изменения:

    function restrictChecked({
      container,
      selector = 'input[type="checkbox"]',
      min = 0,
      max = Infinity,
    }) {
      function onChange({ target: t }) {
        if (t.matches(selector)) {
          const countChecked = this.querySelectorAll(`${selector}:checked`).length;
          t.checked ||= countChecked <  min;
          t.checked &&= countChecked <= max;
        }
      }
    
      container.addEventListener('change', onChange);
    
      return () => container.removeEventListener('change', onChange);
    }
    Ответ написан
    Комментировать
  • Как из родителя сбросить :checked в дочерних компонентах?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Снизу вверх изменения отслеживаются, а сверху вниз - нет, состояние дочерних компонентов никак не зависит от родителя.

    Вырезать обработчики события change и, соответственно, методы filtered; свойства currentPriority и currentStopsId сделать вычисляемыми - геттеры возвращают значение параметра, сеттеры отправляют новое значение родителю:

    currentPriority: {
      get() {
        return this.filterPriorities;
      },
      set(val) {
        this.$emit('update:filterPriorities', val);
      },
    },

    currentStopsId: {
      get() {
        return this.filterStops;
      },
      set(val) {
        this.$emit('update:filterStops', val);
      },
    },
    Ответ написан
    1 комментарий
  • По какому признаку оперделяются ЛУЧШИЕ ВОПРОСЫ ПОЛЬЗОВАТЕЛЯ?

    0xD34F
    @0xD34F
    По количеству пользователей, которые на вопрос подписались. Так мне кажется.
    Ответ написан
    Комментировать
  • Как получить уровни вложенности для всех вложенных объектов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Функции добавить параметр - глубину вложенности.

    Проверяем, чем является значение, если не объект, то возвращаем как есть. Иначе собираем новый объект, в который записываем текущую глубину вложенности и свойства исходного объекта, обработав их значения функцией - делаем рекурсивный вызов с глубиной вложенности, увеличенной на единицу:

    const addDepth = (val, depth = 0) =>
      val instanceof Object
        ? Object.entries(val).reduce((acc, n) => (
            acc[n[0]] = addDepth(n[1], depth + 1),
            acc
          ), { depth })
        : val;
    Ответ написан
    Комментировать