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>
return
перед gen_nums(stop_n, number)
?def gen_nums(max_num, num):
if num <= max_num:
print(num)
gen_nums(max_num, num + 1)
# или
def gen_nums(num):
if num >= 1:
gen_nums(num - 1)
print(num)
const units = [
[ 'де?н', 24 * 60 * 60 ],
[ 'час', 60 * 60 ],
[ 'мин', 60 ],
[ 'сек', 1 ],
].map(n => [ RegExp(`\\d+(?=\\s+${n[0]})`), n[1] ]);
const getSeconds = str =>
units.reduce((acc, n) => acc + n[0].exec(str) * n[1], 0);
getSeconds('2 часа 22 секунды') // 7222
getSeconds('99 минут') // 5940
getSeconds('1 час 1 минута 1 секунда') // 3661
getSeconds('1 день 23 часа 59 минут 60 секунд') // 172800
getSeconds('2 дня') // 172800
getSeconds('546 секунд и ещё 2 минуты') // 666
RegExp(`\\d+(?=\\s+${n[0]})`, 'g')
, а функция подсчёта секунд примет следующий вид:const getSeconds = str =>
units.reduce((seconds, [ reg, multiplier ]) => {
return [...str.matchAll(reg)].reduce((acc, n) => acc + n * multiplier, seconds);
}, 0);
// или
const getSeconds = str => units
.flatMap(n => (str.match(n[0]) ?? []).map(m => m * n[1]))
.reduce((acc, n) => acc + n, 0);
// или
const getSeconds = str => eval(units
.map(n => `${n[1]} * (${str.match(n[0])?.join('+') ?? 0})`)
.join('+')
);
getSeconds('1 секунда плюс 3 секунды плюс 5 секунд') // 9
getSeconds('21 день, 7 дней, да ещё 3 дня - всего секунд в мае месяце будет') // 2678400
<template v-for
, ячейки с rowspan'ами создаются в зависимости от равенства индексов элементов вложенных массивов нулю, значения rowspan'ов - длины (суммы длин) вложенных массивов. Например:const rowspan = item => item.backlinks.reduce((acc, n) => acc + n.recipients.length, 0);
<tbody>
<template v-for="item in data">
<template v-for="(backlink, iBacklink) in item.backlinks">
<template v-for="(recipient, iRecipient) in backlink.recipients">
<tr>
<template v-if="!iBacklink && !iRecipient">
<td :rowspan="rowspan(item)">{{ item.name }}</td>
<td :rowspan="rowspan(item)">{{ item['domain score'] }}</td>
</template>
<template v-if="!iRecipient">
<td :rowspan="backlink.recipients.length">{{ backlink.donor }}</td>
<td :rowspan="backlink.recipients.length">{{ backlink['page score'] }}</td>
</template>
<td>{{ recipient.url }}</td>
<td>{{ recipient.image }}</td>
</tr>
</template>
</template>
</template>
</tbody>
const keys = ref([
'name', 'domain score', 'backlinks',
'donor', 'page score', 'recipients',
'url', 'image',
]);
function createTableData(arr, keys, iKey = 0) {
return arr.flatMap(n => {
const row = [];
const innerRows = [];
for (let i = iKey; i < keys.length; i++) {
const val = n[keys[i]];
if (Array.isArray(val)) {
innerRows.push(...createTableData(val, keys, i + 1));
row.forEach(cell => cell.rowspan = innerRows.length);
row.push(...innerRows.shift());
break;
} else {
row.push({ text: val });
}
}
return [ row, ...innerRows ];
});
}
const tableData = computed(() => {
return createTableData(props.data, props.keys);
});
<tbody>
<tr v-for="row in tableData">
<td
v-for="cell in row"
v-text="cell.text"
:rowspan="cell.rowspan"
></td>
</tr>
</tbody>
li
.const defaultEdit = {
index: -1,
value: '',
};
function App() {
const [ notes, setNotes ] = useState([...'12345']);
const [ edit, setEdit ] = useState(defaultEdit);
const onClick = ({ currentTarget: { dataset: { index } } }) =>
setEdit({ index: +index, value: notes[index] });
const onChange = ({ target: { value } }) =>
setEdit(edit => ({ ...edit, value }));
const onBlur = () => {
setNotes(notes => notes.map((n, i) => i === edit.index ? edit.value : n));
setEdit(defaultEdit);
};
return (
<div>
<ul>{notes.map((n, i) => (
<li data-index={i} onClick={onClick}>
{n}
</li>))}
</ul>
<input value={edit.value} onChange={onChange} onBlur={onBlur} />
</div>
);
}
const coords = ref([ 50, 50 ]);
const circleStyles = computed(() => ({
left: `${coords.value[0]}px`,
top: `${coords.value[1]}px`,
}));
const updateCoords = e => coords.value = [ e.pageX, e.pageY ];
const updateCoordsOn = () => document.addEventListener('mousemove', updateCoords);
const updateCoordsOff = () => document.removeEventListener('mousemove', updateCoords);
<div
class="circle"
:style="circleStyles"
@mousedown="updateCoordsOn"
@mouseup="updateCoordsOff"
></div>
function createTree($data, $params = []) {
extract($params + [
'levelKey' => 'level',
'childrenKey' => 'children',
]);
$root = [];
foreach ($data as $n) {
$arr = &$root;
for (
$level = $data[0][$levelKey];
$level++ < $n[$levelKey];
$arr = &$arr[count($arr) - 1][$childrenKey]
) ;
$arr[] = $n + [ $childrenKey => [] ];
}
return $root;
}
$tree = createTree($arMenu, [ 'levelKey' => 'LEVEL' ]);
function createTree($data, $params = []) {
$levelKey = $params['levelKey'] ?? 'level';
$childrenKey = $params['childrenKey'] ?? 'children';
$root = [];
$stack = [ [ $data[0][$levelKey], &$root ] ];
foreach ($data as $n) {
$end = end($stack);
$level = $n[$levelKey];
if ($level > $end[0]) {
$stack[] = [ $level, &$end[1][count($end[1]) - 1][$childrenKey] ];
} else while ($level < end($stack)[0]) {
array_pop($stack);
}
end($stack)[1][] = array_merge($n, [ $childrenKey => [] ]);
}
return $root;
}
The functions passed to thereducers
parameter can be accessed through thecaseReducers
return field.
playlistSlice.caseReducers.playAudio(state, { payload: state.id });
.panel {
padding: 0 15px;
transition: all 0.4s;
height: 0;
overflow: hidden;
}
.faq-item.active .panel {
padding: 15px 15px 20px;
}
.faq-item.active .cross {
transform: rotate(45deg);
}
const containerSelector = '.faq-list';
const itemSelector = '.faq-item';
const headerSelector = '.accordion';
const contentSelector = '.panel';
const activeClass = 'active';
const toggle = item => item
?.closest(containerSelector)
?.querySelectorAll(itemSelector).forEach(n => {
const state = n === item && !n.classList.contains(activeClass);
const content = n.querySelector(contentSelector);
n.classList.toggle(activeClass, state);
content.style.height = `${state ? content.scrollHeight : 0}px`;
});
document.addEventListener('click', e => {
toggle(e.target.closest(headerSelector)?.closest(itemSelector));
});
// или
document.querySelectorAll(headerSelector).forEach(n => {
n.addEventListener('click', toggle.bind(null, n.closest(itemSelector)));
});
<div v-once>
<select ref="select" v-model="region"></select>
</div>
mounted() {
const choices = new Choices(this.$refs.select);
this.$watch(
'regions',
val => choices.setChoices(val, 'value', 'name', true),
{ immediate: true }
);
this.$on('hook:beforeDestroy', () => choices.destroy());
},
translate
элементу. А чтобы не делать отдельные обработчики клика всем кнопкам, можно записать им в data-атрибут информацию о том, на сколько какие координаты должна изменить данная кнопка.<button data-steps="1,0">right</button>
<button data-steps="-1,0">left</button>
<button data-steps="0,1">down</button>
<button data-steps="0,-1">up</button>
<button data-steps="1,1">right down</button>
<button data-steps="0,-2">double up</button>
const coord = [ 0, 0 ];
const stepSize = 30;
const box = document.querySelector('#box');
const buttons = document.querySelectorAll('[data-steps]');
buttons.forEach(n => n.addEventListener('click', onClick));
function onClick() {
this.dataset.steps.split(',').forEach((n, i) => coord[i] += n * stepSize);
box.style.transform = `translate(${coord.map(n => `${n}px`).join(',')})`;
}