summaryrefslogtreecommitdiff
path: root/packages/client/src/components/form
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-06-28 15:59:49 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-06-28 15:59:49 +0900
commit270e1212aca2931f8c01d43c187106b988fa2c49 (patch)
treec95c503e3279c9771f181441ab086efdbcd92741 /packages/client/src/components/form
parentrefactor(client): use setup syntax (diff)
downloadmisskey-270e1212aca2931f8c01d43c187106b988fa2c49.tar.gz
misskey-270e1212aca2931f8c01d43c187106b988fa2c49.tar.bz2
misskey-270e1212aca2931f8c01d43c187106b988fa2c49.zip
chore(client): refactor and style tweaks
Diffstat (limited to 'packages/client/src/components/form')
-rw-r--r--packages/client/src/components/form/input.vue257
-rw-r--r--packages/client/src/components/form/select.vue268
2 files changed, 213 insertions, 312 deletions
diff --git a/packages/client/src/components/form/input.vue b/packages/client/src/components/form/input.vue
index 5065e28892..ec1ad20de3 100644
--- a/packages/client/src/components/form/input.vue
+++ b/packages/client/src/components/form/input.vue
@@ -33,176 +33,118 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/ui/button.vue';
import { useInterval } from '@/scripts/use-interval';
-export default defineComponent({
- components: {
- MkButton,
- },
+const props = defineProps<{
+ modelValue: string | number;
+ type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time';
+ required?: boolean;
+ readonly?: boolean;
+ disabled?: boolean;
+ pattern?: string;
+ placeholder?: string;
+ autofocus?: boolean;
+ autocomplete?: boolean;
+ spellcheck?: boolean;
+ step?: any;
+ datalist?: string[];
+ inline?: boolean;
+ debounce?: boolean;
+ manualSave?: boolean;
+ small?: boolean;
+ large?: boolean;
+}>();
- props: {
- modelValue: {
- required: true,
- },
- type: {
- type: String,
- required: false,
- },
- required: {
- type: Boolean,
- required: false,
- },
- readonly: {
- type: Boolean,
- required: false,
- },
- disabled: {
- type: Boolean,
- required: false,
- },
- pattern: {
- type: String,
- required: false,
- },
- placeholder: {
- type: String,
- required: false,
- },
- autofocus: {
- type: Boolean,
- required: false,
- default: false,
- },
- autocomplete: {
- required: false,
- },
- spellcheck: {
- required: false,
- },
- step: {
- required: false,
- },
- datalist: {
- type: Array,
- required: false,
- },
- inline: {
- type: Boolean,
- required: false,
- default: false,
- },
- debounce: {
- type: Boolean,
- required: false,
- default: false,
- },
- manualSave: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
+const emit = defineEmits<{
+ (ev: 'change', _ev: KeyboardEvent): void;
+ (ev: 'keydown', _ev: KeyboardEvent): void;
+ (ev: 'enter'): void;
+ (ev: 'update:modelValue', value: string | number): void;
+}>();
- emits: ['change', 'keydown', 'enter', 'update:modelValue'],
+const { modelValue, type, autofocus } = toRefs(props);
+const v = ref(modelValue.value);
+const id = Math.random().toString(); // TODO: uuid?
+const focused = ref(false);
+const changed = ref(false);
+const invalid = ref(false);
+const filled = computed(() => v.value !== '' && v.value != null);
+const inputEl = ref<HTMLElement>();
+const prefixEl = ref<HTMLElement>();
+const suffixEl = ref<HTMLElement>();
+const height =
+ props.small ? 38 :
+ props.large ? 42 :
+ 40;
- setup(props, context) {
- const { modelValue, type, autofocus } = toRefs(props);
- const v = ref(modelValue.value);
- const id = Math.random().toString(); // TODO: uuid?
- const focused = ref(false);
- const changed = ref(false);
- const invalid = ref(false);
- const filled = computed(() => v.value !== '' && v.value != null);
- const inputEl = ref<HTMLElement>();
- const prefixEl = ref<HTMLElement>();
- const suffixEl = ref<HTMLElement>();
+const focus = () => inputEl.value.focus();
+const onInput = (ev: KeyboardEvent) => {
+ changed.value = true;
+ emit('change', ev);
+};
+const onKeydown = (ev: KeyboardEvent) => {
+ emit('keydown', ev);
- const focus = () => inputEl.value.focus();
- const onInput = (ev) => {
- changed.value = true;
- context.emit('change', ev);
- };
- const onKeydown = (ev: KeyboardEvent) => {
- context.emit('keydown', ev);
-
- if (ev.code === 'Enter') {
- context.emit('enter');
- }
- };
-
- const updated = () => {
- changed.value = false;
- if (type.value === 'number') {
- context.emit('update:modelValue', parseFloat(v.value));
- } else {
- context.emit('update:modelValue', v.value);
- }
- };
+ if (ev.code === 'Enter') {
+ emit('enter');
+ }
+};
- const debouncedUpdated = debounce(1000, updated);
+const updated = () => {
+ changed.value = false;
+ if (type.value === 'number') {
+ emit('update:modelValue', parseFloat(v.value));
+ } else {
+ emit('update:modelValue', v.value);
+ }
+};
- watch(modelValue, newValue => {
- v.value = newValue;
- });
+const debouncedUpdated = debounce(1000, updated);
- watch(v, newValue => {
- if (!props.manualSave) {
- if (props.debounce) {
- debouncedUpdated();
- } else {
- updated();
- }
- }
+watch(modelValue, newValue => {
+ v.value = newValue;
+});
- invalid.value = inputEl.value.validity.badInput;
- });
+watch(v, newValue => {
+ if (!props.manualSave) {
+ if (props.debounce) {
+ debouncedUpdated();
+ } else {
+ updated();
+ }
+ }
- // このコンポーネントが作成された時、非表示状態である場合がある
- // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
- useInterval(() => {
- if (prefixEl.value) {
- if (prefixEl.value.offsetWidth) {
- inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
- }
- }
- if (suffixEl.value) {
- if (suffixEl.value.offsetWidth) {
- inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
- }
- }
- }, 100, {
- immediate: true,
- afterMounted: true,
- });
+ invalid.value = inputEl.value.validity.badInput;
+});
- onMounted(() => {
- nextTick(() => {
- if (autofocus.value) {
- focus();
- }
- });
- });
+// このコンポーネントが作成された時、非表示状態である場合がある
+// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
+useInterval(() => {
+ if (prefixEl.value) {
+ if (prefixEl.value.offsetWidth) {
+ inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
+ }
+ }
+ if (suffixEl.value) {
+ if (suffixEl.value.offsetWidth) {
+ inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
+ }
+ }
+}, 100, {
+ immediate: true,
+ afterMounted: true,
+});
- return {
- id,
- v,
- focused,
- invalid,
- changed,
- filled,
- inputEl,
- prefixEl,
- suffixEl,
- focus,
- onInput,
- onKeydown,
- updated,
- };
- },
+onMounted(() => {
+ nextTick(() => {
+ if (autofocus.value) {
+ focus();
+ }
+ });
});
</script>
@@ -229,14 +171,13 @@ export default defineComponent({
}
> .input {
- $height: 42px;
position: relative;
> input {
appearance: none;
-webkit-appearance: none;
display: block;
- height: $height;
+ height: v-bind("height + 'px'");
width: 100%;
margin: 0;
padding: 0 12px;
@@ -266,7 +207,7 @@ export default defineComponent({
top: 0;
padding: 0 12px;
font-size: 1em;
- height: $height;
+ height: v-bind("height + 'px'");
pointer-events: none;
&:empty {
diff --git a/packages/client/src/components/form/select.vue b/packages/client/src/components/form/select.vue
index 7f5f8784b6..05e95a0917 100644
--- a/packages/client/src/components/form/select.vue
+++ b/packages/client/src/components/form/select.vue
@@ -26,178 +26,139 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue';
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
-export default defineComponent({
- components: {
- MkButton,
- },
+const props = defineProps<{
+ modelValue: string;
+ required?: boolean;
+ readonly?: boolean;
+ disabled?: boolean;
+ placeholder?: string;
+ autofocus?: boolean;
+ inline?: boolean;
+ manualSave?: boolean;
+ small?: boolean;
+ large?: boolean;
+}>();
- props: {
- modelValue: {
- required: true,
- },
- required: {
- type: Boolean,
- required: false,
- },
- readonly: {
- type: Boolean,
- required: false,
- },
- disabled: {
- type: Boolean,
- required: false,
- },
- placeholder: {
- type: String,
- required: false,
- },
- autofocus: {
- type: Boolean,
- required: false,
- default: false,
- },
- inline: {
- type: Boolean,
- required: false,
- default: false,
- },
- manualSave: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
+const emit = defineEmits<{
+ (ev: 'change', _ev: KeyboardEvent): void;
+ (ev: 'update:modelValue', value: string): void;
+}>();
- emits: ['change', 'update:modelValue'],
+const slots = useSlots();
- setup(props, context) {
- const { modelValue, autofocus } = toRefs(props);
- const v = ref(modelValue.value);
- const focused = ref(false);
- const changed = ref(false);
- const invalid = ref(false);
- const filled = computed(() => v.value !== '' && v.value != null);
- const inputEl = ref(null);
- const prefixEl = ref(null);
- const suffixEl = ref(null);
- const container = ref(null);
+const { modelValue, autofocus } = toRefs(props);
+const v = ref(modelValue.value);
+const focused = ref(false);
+const changed = ref(false);
+const invalid = ref(false);
+const filled = computed(() => v.value !== '' && v.value != null);
+const inputEl = ref(null);
+const prefixEl = ref(null);
+const suffixEl = ref(null);
+const container = ref(null);
+const height =
+ props.small ? 38 :
+ props.large ? 42 :
+ 40;
- const focus = () => inputEl.value.focus();
- const onInput = (ev) => {
- changed.value = true;
- context.emit('change', ev);
- };
+const focus = () => inputEl.value.focus();
+const onInput = (ev) => {
+ changed.value = true;
+ emit('change', ev);
+};
- const updated = () => {
- changed.value = false;
- context.emit('update:modelValue', v.value);
- };
+const updated = () => {
+ changed.value = false;
+ emit('update:modelValue', v.value);
+};
- watch(modelValue, newValue => {
- v.value = newValue;
- });
+watch(modelValue, newValue => {
+ v.value = newValue;
+});
- watch(v, newValue => {
- if (!props.manualSave) {
- updated();
- }
+watch(v, newValue => {
+ if (!props.manualSave) {
+ updated();
+ }
- invalid.value = inputEl.value.validity.badInput;
- });
+ invalid.value = inputEl.value.validity.badInput;
+});
- // このコンポーネントが作成された時、非表示状態である場合がある
- // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
- useInterval(() => {
- if (prefixEl.value) {
- if (prefixEl.value.offsetWidth) {
- inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
- }
- }
- if (suffixEl.value) {
- if (suffixEl.value.offsetWidth) {
- inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
- }
- }
- }, 100, {
- immediate: true,
- afterMounted: true,
- });
+// このコンポーネントが作成された時、非表示状態である場合がある
+// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
+useInterval(() => {
+ if (prefixEl.value) {
+ if (prefixEl.value.offsetWidth) {
+ inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
+ }
+ }
+ if (suffixEl.value) {
+ if (suffixEl.value.offsetWidth) {
+ inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
+ }
+ }
+}, 100, {
+ immediate: true,
+ afterMounted: true,
+});
- onMounted(() => {
- nextTick(() => {
- if (autofocus.value) {
- focus();
- }
- });
- });
+onMounted(() => {
+ nextTick(() => {
+ if (autofocus.value) {
+ focus();
+ }
+ });
+});
+
+const onClick = (ev: MouseEvent) => {
+ focused.value = true;
- const onClick = (ev: MouseEvent) => {
- focused.value = true;
+ const menu = [];
+ let options = slots.default!();
- const menu = [];
- let options = context.slots.default();
+ const pushOption = (option: VNode) => {
+ menu.push({
+ text: option.children,
+ active: v.value === option.props.value,
+ action: () => {
+ v.value = option.props.value;
+ },
+ });
+ };
- const pushOption = (option: VNode) => {
+ const scanOptions = (options: VNode[]) => {
+ for (const vnode of options) {
+ if (vnode.type === 'optgroup') {
+ const optgroup = vnode;
menu.push({
- text: option.children,
- active: v.value === option.props.value,
- action: () => {
- v.value = option.props.value;
- },
+ type: 'label',
+ text: optgroup.props.label,
});
- };
-
- const scanOptions = (options: VNode[]) => {
- for (const vnode of options) {
- if (vnode.type === 'optgroup') {
- const optgroup = vnode;
- menu.push({
- type: 'label',
- text: optgroup.props.label,
- });
- scanOptions(optgroup.children);
- } else if (Array.isArray(vnode.children)) { // 何故かフラグメントになってくることがある
- const fragment = vnode;
- scanOptions(fragment.children);
- } else {
- const option = vnode;
- pushOption(option);
- }
- }
- };
-
- scanOptions(options);
+ scanOptions(optgroup.children);
+ } else if (Array.isArray(vnode.children)) { // 何故かフラグメントになってくることがある
+ const fragment = vnode;
+ scanOptions(fragment.children);
+ } else {
+ const option = vnode;
+ pushOption(option);
+ }
+ }
+ };
- os.popupMenu(menu, container.value, {
- width: container.value.offsetWidth,
- }).then(() => {
- focused.value = false;
- });
- };
+ scanOptions(options);
- return {
- v,
- focused,
- invalid,
- changed,
- filled,
- inputEl,
- prefixEl,
- suffixEl,
- container,
- focus,
- onInput,
- onClick,
- updated,
- };
- },
-});
+ os.popupMenu(menu, container.value, {
+ width: container.value.offsetWidth,
+ }).then(() => {
+ focused.value = false;
+ });
+};
</script>
<style lang="scss" scoped>
@@ -223,7 +184,6 @@ export default defineComponent({
}
> .input {
- $height: 42px;
position: relative;
cursor: pointer;
@@ -237,7 +197,7 @@ export default defineComponent({
appearance: none;
-webkit-appearance: none;
display: block;
- height: $height;
+ height: v-bind("height + 'px'");
width: 100%;
margin: 0;
padding: 0 12px;
@@ -265,7 +225,7 @@ export default defineComponent({
top: 0;
padding: 0 12px;
font-size: 1em;
- height: $height;
+ height: v-bind("height + 'px'");
pointer-events: none;
&:empty {