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>;
}
работает, но кажется, что достаточно криво
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);
v-model
, кого показывать в модальном окне - отправляйте наверх эту информацию вместе с событием. Свойство, управляющее видимостью модального окна - пусть оно вместо логического значения хранит id или объект или что там у вас должно показываться в окне, если не null
, значит открываем окно. Вот так всё просто.v-model
, конечно только на уровне таблицы, в строках всё по-прежнему.<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();
});
const addCard = (cardName) => { cards.value.push({ id: Date.now(), component: cardName, order: cards.value.length + 1, isRequired: false }) }
const components = {
ShortTextCard,
LongTextCard,
SingleQuestionCard,
MultiQuestionCard,
};
- @click="addCard(ShortTextCard)"
+ @click="addCard('ShortTextCard')"
- :is="card.component"
+ :is="components[card.component]"
<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>
handleEdit(row){
@click="handleEdit(scope.$index, scope.row)"
path:'/protocol_information/:id/edit/', params:{ id:row.id }
path: `/protocol_information/${row.id}/edit/`,
const obj1 = Object.fromEntries(arr1.map(n => [ n.name, n ]));
const newArr2 = arr2.map(n => ({ ...obj1[n.name], ...n }));
arr2.forEach(n => {
const obj = obj1[n.name];
if (obj) {
n.id = obj.id;
}
});
можно ли исключить объекты у которых не изменился value? То есть что бы этих объектов не было в итоговом массиве.
const newArr2 = arr2.reduce((acc, n) => (
obj1[n.name]?.value !== n.value && acc.push({ ...obj1[n.name], ...n }),
acc
), []);
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));
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);
}
currentPriority: {
get() {
return this.filterPriorities;
},
set(val) {
this.$emit('update:filterPriorities', val);
},
},
currentStopsId: {
get() {
return this.filterStops;
},
set(val) {
this.$emit('update:filterStops', val);
},
},
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;
const toString = (val, keys = []) =>
val instanceof Object
? Object.entries(val).map(([ k, v ]) => {
keys.push(k);
const result = toString(v, keys);
keys.pop();
return result;
}).join('&')
: `${keys.join('.')}=${val}`;
function toString(val) {
const result = [];
for (const stack = [ [ val, [] ] ]; stack.length; ) {
const [ n, keys ] = stack.pop();
if (n instanceof Object) {
stack.push(...Object.entries(n).map(([ k, v ]) => [ v, [ ...keys, k ] ]).reverse());
} else {
result.push(`${keys.join('.')}=${n}`);
}
}
return result.join('&');
}
на любом языке
v-for="(address, i) in form.addresses_to" :key="i"
всегда удаляется последний
Vue использует алгоритм, минимизирующий перемещение элементов
const formMeta = ref([
{
name: '...',
component: '...',
props: { ... },
},
...
]);
name
элементов formMeta
. Изначально можно сделать пустым: const formData = ref({});
. Можно явно задать начальные значения:const formData = ref({
имяПоля1: значение1,
имяПоля2: значение2,
...
});
const formData = ref(Object.fromEntries(formMeta.value.map(n => [
n.name,
n.defaultValue ?? null,
])));
formMeta
создаётся форма, через v-model
свойства formData
связываются с экземплярами компонентов:<form>
<div v-for="n in formMeta">
<component
:is="components[n.component]"
v-model="formData[n.name]"
v-bind="n.props"
/>
</div>
</form>