Skip to content

Commit

Permalink
Merge pull request #578 from Tencent/dev
Browse files Browse the repository at this point in the history
v3.15.0
  • Loading branch information
Maizify committed Nov 2, 2022
2 parents 5933fb4 + 3d53d44 commit 05d8039
Show file tree
Hide file tree
Showing 41 changed files with 1,810 additions and 157 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
English | [简体中文](./CHANGELOG_CN.md)

## 3.15.0 (2022-11-02)

- `Feat(Log)` Add recycle scrolling to imporove performance, and add scroll to top/bottom buttons. (PR #570)
- `Feat(Log)` Add support for `console.group(), console.groupCollapsed(), console.groupEnd()`. (issue #545)
- `Feat(Network)` Add recycle scrolling to imporove performance.
- `Feat(Network)` Add "Start Time" of a request.
- `Feat(Network)` Use `curl` instead of `url` as the copy value of a request. (issue #410)
- `Fix(Storage)` Fix an event bug that overflow content cannot scroll. (issue #542)
- `Fix(Core)` Fix click event on `<select>` elements. (PR #577)


## 3.14.7 (2022-09-23)

- `Perf(Log)` Optimize rendering performance when adding logs. (PR #567)
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG_CN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
[English](./CHANGELOG.md) | 简体中文

## 3.15.0 (2022-11-02)

- `Feat(Log)` 新增虚拟滚动列表以提升性能,并支持快速滚动到顶部/底部。 (PR #570)
- `Feat(Log)` 新增对 `console.group(), console.groupCollapsed(), console.groupEnd()` 方法的支持。 (issue #545)
- `Feat(Network)` 新增虚拟滚动列表以提升性能。
- `Feat(Network)` 新增 request 的 "Start Time"(发起时间)。
- `Feat(Network)` 使用 `curl` 格式作为 request 的复制内容,而非 `url`。 (issue #410)
- `Fix(Storage)` 修复内容溢出的元素无法滑动的问题。 (issue #542)
- `Fix(Core)` 修复 `<select>` 的点击事件问题。 (PR #577)


## 3.14.7 (2022-09-23)

- `Perf(Log)` 优化打印日志时的性能。 (PR #567)
Expand Down
20 changes: 20 additions & 0 deletions dev/log.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<a onclick="logTypes()" href="javascript:;" class="weui_btn weui_btn_default">Log/Info/Debug/Warn/Error</a>
<a onclick="differentPanelLog()" href="javascript:;" class="weui_btn weui_btn_default">Output to Different Panels</a>
<a onclick="logTime()" href="javascript:;" class="weui_btn weui_btn_default">console.time</a>
<a onclick="logGroup()" href="javascript:;" class="weui_btn weui_btn_default">console.group</a>
</div>

<div class="section">
Expand Down Expand Up @@ -160,6 +161,25 @@
console.log('Wait...', i);
}
console.timeEnd(label);
console.timeEnd(label);
}

function logGroup() {
vConsole.show();
console.log('This is the outer level');
console.group();
console.log('Level 2');
console.group(aa);
console.log('Level 3');
console.groupCollapsed('LV4');
console.warn('More of level 4');
console.info(aa);
console.groupEnd();
console.log('Back to level 3')
console.groupEnd();
console.log('Back to level 2');
console.groupEnd();
console.log('Back to the outer level');
}

function formattingLog() {
Expand Down
49 changes: 49 additions & 0 deletions dev/network.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<div class="hd">XMLHttpRequest</div>

<a onclick="getAjax()" href="javascript:;" class="weui_btn weui_btn_default">GET XHR</a>
<a onclick="putAjax()" href="javascript:;" class="weui_btn weui_btn_default">PUT XHR</a>
<a onclick="postAjax()" href="javascript:;" class="weui_btn weui_btn_default">POST XHR (Data: Object)</a>
<a onclick="postAjax('json')" href="javascript:;" class="weui_btn weui_btn_default">POST XHR (Data: JSON String)</a>
<a onclick="postAjax('', 'massive.json')" href="javascript:;" class="weui_btn weui_btn_default">POST XHR (Massive Resp)</a>
Expand All @@ -34,6 +35,7 @@
<a onclick="getFetchText()" href="javascript:;" class="weui_btn weui_btn_default">GET Fetch Text</a>
<a onclick="getFetchJsonText()" href="javascript:;" class="weui_btn weui_btn_default">GET Fetch JSON Text</a>
<a onclick="getFetch500()" href="javascript:;" class="weui_btn weui_btn_default">GET Fetch 500</a>
<a onclick="putFetch()" href="javascript:;" class="weui_btn weui_btn_default">PUT Fetch</a>
<a onclick="postFetch()" href="javascript:;" class="weui_btn weui_btn_default">POST Fetch</a>
<a onclick="postFetchByRequest()" href="javascript:;" class="weui_btn weui_btn_default">POST Fetch Using XHR</a>
<a onclick="optionsFetch()" href="javascript:;" class="weui_btn weui_btn_default">OPTIONS Fetch</a>
Expand Down Expand Up @@ -138,6 +140,30 @@
};
}

function putAjax() {
showPanel();
const url = window.location.origin + '/dev/data/success.json?method=xhrGet&id=' + Math.random() + '&<xss0>';

let postData = {
foo: 'bar',
book: { id: 123, type: 'comic', uid: [4,5,6,7,8] },
id: `${Math.random()}${Math.random()}${Math.random()}${Math.random()}${Math.random()}${Math.random()}`,
type: 'xhr',
'<xss0>': '<xss1> XSS Attack!'
};

const xhr = new XMLHttpRequest();
xhr.open('PUT', url);
xhr.setRequestHeader('custom-header', 'foobar');
xhr.send(postData);
xhr.onload = () => {
console.log('put XHR Response:', JSON.parse(xhr.response));
};
xhr.onerror = () => {
console.log('put XHR Error');
};
}

function getFetch() {
showPanel();
window.fetch('./data/success.json?method=fetchGet&id=' + Math.random(), {
Expand Down Expand Up @@ -185,6 +211,29 @@
});
}

function putFetch() {
showPanel();
window.fetch('./data/success.json?method=fetchPut&id=' + Math.random(), {
method: 'PUT',
headers: {
'custom-header': 'foobar',
'content-type': 'application/json'
},
body: { foo: 'bar', id: Math.random(), type: 'fetch' },
}).then((data) => {
console.log('putFetch() response:', data);
setTimeout(() => {
data.json().then((res) => {
console.log(res);
});
}, 3000);
// return data;
// return data.json();
}).then((data) => {
// console.log('putFetch() json:', data);
});
}

function postFetch() {
window.fetch('./data/success.json?method=fetchPost&<xss2>x=<xss2>', {
method: 'POST',
Expand Down
8 changes: 7 additions & 1 deletion doc/plugin_event_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ Trigger while vConsole trying to create a new tab for a plugin. This event will
After binding this event, vConsole will get HTML from your callback to render a tab. A new tab will definitely be added if you bind this event, no matter what tab's HTML you set. Do not bind this event if you don't want to add a new tab.

##### Callback Arguments:
- (required) function(html): a callback function that receives the content HTML of the new tab. `html` can be a HTML `string` or an `HTMLElement` object (Or object which supports `appendTo()` method, like JQuery object).
- (required) function(html, options): a callback function that receives the content HTML of the new tab. `html` can be a HTML `string` or an `HTMLElement` object (Or object which supports `appendTo()` method, like JQuery object), and an optional `object` for tab options.

A tab option is an object with properties:

Property | | | |
------- | ------- | ------- | -------
fixedHeight | boolean | optional | Whether the height of tab is fixed to 100%.

##### Example:

Expand Down
8 changes: 7 additions & 1 deletion doc/plugin_event_list_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ myPlugin.on('init', function() {
绑定此事件后,vConsole 会认为此插件需要创建新 tab,并会将 callback 中获取的 HTML 用于渲染 tab。因此,只要绑定了此事件,新 tab 肯定会被渲染到页面中,无论 callback 传入的 HTML 是否为空。如果不需要添加新 tab,请不要绑定此事件。

##### Callback 参数
- (必填) function(html): 回调函数,接收一个 HTML 参数用于渲染 tab。`html` 可以为 HTML 字符串,或者 `HTMLElement` 对象(或支持 `appendTo()` 方法的对象,如 jQuery 对象)。
- (必填) function(html, options): 回调函数,第一个参数接收一个 HTML 参数用于渲染 tab。`html` 可以为 HTML 字符串,或者 `HTMLElement` 对象(或支持 `appendTo()` 方法的对象,如 jQuery 对象)。第二个参数接收一个可选配置信息。

配置的参数为:

Property | | | |
------- | ------- | ------- | -------
fixedHeight | boolean | 选填 | tab 高度固定为 100%。

##### 例子:

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vconsole",
"version": "3.14.7",
"version": "3.15.0",
"description": "A lightweight, extendable front-end developer tool for mobile web page.",
"homepage": "https://github.com/Tencent/vConsole",
"files": [
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import copy from 'copy-text-to-clipboard';
import * as tool from '../lib/tool';
import Icon from '../component/icon.svelte';
import * as tool from '../../lib/tool';
import Icon from './icon.svelte';
export let content: any = '';
export let handler: (content: any) => string = undefined;
Expand Down
36 changes: 36 additions & 0 deletions src/component/recycleScroller/recycleItem.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
export let show: boolean;
export let top: boolean;
export let onResize: (height: number) => void = () => {};
let item: HTMLDivElement | undefined;
let observer: ResizeObserver | null = null;
onMount(() => {
if (show) {
onResize(item.getBoundingClientRect().height);
}
observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (show) onResize(entry.contentRect.height)
});
observer.observe(item);
});
onDestroy(() => {
observer.disconnect();
});
</script>

<div
bind:this={item}
class="vc-scroller-item"
style:display={show ? "block" : "none"}
style:top="{top}px"
>
<slot />
</div>
161 changes: 161 additions & 0 deletions src/component/recycleScroller/recycleManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
const createRecycleManager = () => {
const recycles: { key: number; index: number; show: boolean }[] = [];

const poolKeys: number[] = [];
let poolStartIndex = 0;
let poolEndIndex = 0;

let lastItemCount = 0;
let lastStart = 0;
let lastEnd = 0;

const update = (itemCount: number, start: number, end: number) => {
if (lastItemCount === itemCount && lastStart === start && lastEnd === end)
return recycles;

const poolCount = poolKeys.length;

// 计算新的 visible 区域
const newFirstPool =
start <= poolEndIndex
? // 1. 开头一定在 [0, start]
Math.max(
0,
Math.min(
start,
// 2. 开头一定在 [poolStartIndex, poolEndIndex) 之间
Math.max(
poolStartIndex,
Math.min(poolEndIndex - 1, end - poolCount)
)
)
)
: start; // poolEndIndex 如果比 start 小,则前部无法保留下来

const newLastPool =
poolStartIndex <= end
? // 1. 结尾一定在 [end, itemCount] 之间
Math.max(
end,
Math.min(
itemCount,
// 2. 结尾一定在 (poolStartIndex, poolEndIndex] 之间
Math.max(
poolStartIndex + 1,
Math.min(poolEndIndex, newFirstPool + poolCount)
)
)
)
: end; // end 如果比 poolStartIndex 小,则后部无法保留下来

if (poolCount === 0 || newLastPool - newFirstPool < poolCount) {
// 无法复用,全都重新生成
const count = (recycles.length = poolKeys.length = end - start);
for (let i = 0; i < count; i += 1) {
poolKeys[i] = i;
recycles[i] = {
key: i,
index: i + start,
show: true,
};
}
poolStartIndex = start;
poolEndIndex = end;
lastItemCount = itemCount;
lastStart = start;
lastEnd = end;
return recycles;
}

let usedPoolIndex = 0;
let usedPoolOffset = 0;

// 复用区域
let reuseStart = 0;
let reuseEnd = 0;

if (poolEndIndex < newFirstPool || newLastPool < poolStartIndex) {
// 完全没有交集,随便复用
reuseStart = newFirstPool;
reuseEnd = newFirstPool + poolCount;
} else if (poolStartIndex < newFirstPool) {
// 开头复用
usedPoolOffset = newFirstPool - poolStartIndex;
reuseStart = newFirstPool;
reuseEnd = newFirstPool + poolCount;
} else if (newLastPool < poolEndIndex) {
// 尾部复用
usedPoolOffset = poolCount - (poolEndIndex - newLastPool);
reuseStart = newLastPool - poolCount;
reuseEnd = newLastPool;
} else if (newFirstPool <= poolStartIndex && poolEndIndex <= newLastPool) {
// 新的 visible 是完全子集,直接复用
reuseStart = poolStartIndex;
reuseEnd = poolEndIndex;
}

// 开头不可见区域
// 如果有不可见区域,则一定是来自上一次 visible 的复用 row
for (let i = newFirstPool; i < start; i += 1, usedPoolIndex += 1) {
const poolKey = poolKeys[(usedPoolOffset + usedPoolIndex) % poolCount];
const recycle = recycles[i - newFirstPool];
recycle.key = poolKey;
recycle.index = i;
recycle.show = false;
}

// 可见区域
for (let i = start, keyIndex = 0; i < end; i += 1) {
let poolKey: number;
if (reuseStart <= i && i < reuseEnd) {
// 复用 row
poolKey = poolKeys[(usedPoolOffset + usedPoolIndex) % poolCount];
usedPoolIndex += 1;
} else {
// 新建 row
poolKey = poolCount + keyIndex;
keyIndex += 1;
}
const recycleIndex = i - newFirstPool;
if (recycleIndex < recycles.length) {
const recycle = recycles[recycleIndex];
recycle.key = poolKey;
recycle.index = i;
recycle.show = true;
} else {
recycles.push({
key: poolKey,
index: i,
show: true,
});
}
}

// 末尾不可见区域
// 如果有不可见区域,则一定是来自上一次 visible 的复用 row
for (let i = end; i < newLastPool; i += 1, usedPoolIndex += 1) {
const poolKey = poolKeys[(usedPoolOffset + usedPoolIndex) % poolCount];
const recycle = recycles[i - newFirstPool];
recycle.key = poolKey;
recycle.index = i;
recycle.show = false;
}

// 更新 poolKeys
for (let i = 0; i < recycles.length; i += 1) {
poolKeys[i] = recycles[i].key;
}
recycles.sort((a, b) => a.key - b.key);
poolStartIndex = newFirstPool;
poolEndIndex = newLastPool;
lastItemCount = itemCount;
lastStart = start;
lastEnd = end;

return recycles;
};

return update;
};

export default createRecycleManager;
Loading

0 comments on commit 05d8039

Please sign in to comment.