Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: refactor more components to use setup to simplify them (part 3) #1595

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 51 additions & 77 deletions client/src/components/ClickToEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,93 +20,67 @@
</div>
</template>

<script lang="ts">
import { defineComponent, ref, nextTick, Ref } from "vue";

function valueFormatterDefault(value: number): string {
return value.toString();
}

function valueParserDefault(value: string): number {
return parseInt(value, 10);
}

<script lang="ts" setup generic="T extends string | number">
/**
* Provides a value display that can be clicked to edit.
*/
const ClickToEdit = defineComponent({
name: "ClickToEdit",
emits: ["change", "update:modelValue"],
props: {
modelValue: {
type: [String, Number],
required: true,
},
valueFormatter: {
type: Function,
default: valueFormatterDefault,
},
valueParser: {
type: Function,
default: valueParserDefault,
},
},
setup(props, { emit }) {
const editor = ref<HTMLInputElement | undefined>();
const valueFormatter = ref(props.valueFormatter) as Ref<(value: number) => string>;
const valueParser = ref(props.valueParser) as Ref<(value: string) => number>;
import { ref, nextTick, Ref } from "vue";

const editing = ref(false);
const valueDirty = ref();
const display: Ref<HTMLDivElement | undefined> = ref();
const editorWidth = ref(120);
const props = withDefaults(
defineProps<{
valueFormatter?: (value: number) => string;
valueParser?: (value: string) => number;
}>(),
{
valueFormatter: (value: number): string => value.toString(),
valueParser: (value: string): number => parseInt(value, 10),
}
);

async function activate() {
if (display.value) {
editorWidth.value = display.value.offsetWidth + 24;
}
console.info("modelValue", props.modelValue);
if (typeof props.modelValue === "number") {
valueDirty.value = valueFormatter.value(props.modelValue);
} else {
valueDirty.value = props.modelValue;
}
editing.value = true;
await nextTick();
editor.value?.focus();
}
const model = defineModel<T>();

function apply() {
let outValue: string | number;
if (typeof props.modelValue === "number") {
outValue = valueParser.value(valueDirty.value);
} else {
outValue = valueDirty.value;
}
editing.value = false;
emit("change", outValue);
emit("update:modelValue", outValue);
}
const emit = defineEmits<{
change: [value: T];
}>();

function abort() {
editing.value = false;
}
const editor = ref<HTMLInputElement | undefined>();
const valueFormatter = ref(props.valueFormatter) as Ref<(value: number) => string>;
const valueParser = ref(props.valueParser) as Ref<(value: string) => number>;

return {
editor,
display,
editing,
valueDirty,
editorWidth,
const editing = ref(false);
const valueDirty = ref("");
const display: Ref<HTMLDivElement | undefined> = ref();
const editorWidth = ref(120);

activate,
apply,
abort,
};
},
});
async function activate() {
if (display.value) {
editorWidth.value = display.value.offsetWidth + 24;
}
if (typeof model.value === "number") {
valueDirty.value = valueFormatter.value(model.value);
} else {
valueDirty.value = model.value ?? "";
}

Check warning on line 63 in client/src/components/ClickToEdit.vue

View check run for this annotation

Codecov / codecov/patch

client/src/components/ClickToEdit.vue#L62-L63

Added lines #L62 - L63 were not covered by tests
editing.value = true;
await nextTick();
editor.value?.focus();
}

function apply() {
let outValue: T;
if (typeof model.value === "number") {
outValue = valueParser.value(valueDirty.value) as T;
} else {
outValue = valueDirty.value as T;
}
editing.value = false;
model.value = outValue;
emit("change", outValue);
}

Check warning on line 79 in client/src/components/ClickToEdit.vue

View check run for this annotation

Codecov / codecov/patch

client/src/components/ClickToEdit.vue#L69-L79

Added lines #L69 - L79 were not covered by tests

export default ClickToEdit;
function abort() {
editing.value = false;
}

Check warning on line 83 in client/src/components/ClickToEdit.vue

View check run for this annotation

Codecov / codecov/patch

client/src/components/ClickToEdit.vue#L81-L83

Added lines #L81 - L83 were not covered by tests
</script>

<style lang="scss">
Expand Down
77 changes: 30 additions & 47 deletions client/src/components/ClientSettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,19 @@

<v-card-actions>
<v-spacer />
<v-btn color="primary" text @click="applySettings">
<v-btn color="primary" @click="applySettings">
{{ $t("common.save") }}
</v-btn>
<v-btn text @click="cancelSettings">
<v-btn @click="cancelSettings">
{{ $t("common.cancel") }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script lang="ts">
import { defineComponent, Ref, ref, watch } from "vue";
<script lang="ts" setup>
import { Ref, ref, watch } from "vue";
import { useStore } from "@/store";
import { SettingsState, RoomLayoutMode, Theme } from "@/stores/settings";
import _ from "lodash";
Expand All @@ -73,54 +73,37 @@ type ExcludedFields = "volume" | "locale";
type ExposedSettings = Omit<SettingsState, ExcludedFields>;
const EXCLUDED: ExcludedFields[] = ["volume", "locale"];

export const ClientSettingsDialog = defineComponent({
name: "ClientSettingsDialog",
setup() {
const show = ref(false);
const store = useStore();
const settings: Ref<ExposedSettings> = ref(loadSettings());
const sfx = useSfx();
const show = ref(false);
const store = useStore();
const settings: Ref<ExposedSettings> = ref(loadSettings());
const sfx = useSfx();

function loadSettings(): ExposedSettings {
const copy = _.cloneDeep(store.state.settings);
const filtered = _.omit(copy, EXCLUDED);
return filtered;
}
function loadSettings(): ExposedSettings {
const copy = _.cloneDeep(store.state.settings);
const filtered = _.omit(copy, EXCLUDED);
return filtered;
}

function applySettings() {
store.commit("settings/UPDATE", settings.value);
show.value = false;
}
function applySettings() {
store.commit("settings/UPDATE", settings.value);
show.value = false;
}

function cancelSettings() {
show.value = false;
}
function cancelSettings() {
show.value = false;
}

watch(show, () => {
settings.value = loadSettings();
});

store.subscribe(mutation => {
if (mutation.type === "settings/UPDATE") {
sfx.enabled = store.state.settings.sfxEnabled;
sfx.volume.value = store.state.settings.sfxVolume;
}
});

return {
show,
settings,

applySettings,
cancelSettings,
RoomLayoutMode,
Theme,
watch(show, () => {
settings.value = loadSettings();
});

layouts: enumKeys(RoomLayoutMode),
themes: enumKeys(Theme),
};
},
store.subscribe(mutation => {
if (mutation.type === "settings/UPDATE") {
sfx.enabled = store.state.settings.sfxEnabled;
sfx.volume.value = store.state.settings.sfxVolume;
}
});

export default ClientSettingsDialog;
const layouts = enumKeys(RoomLayoutMode);
const themes = enumKeys(Theme);
</script>
Loading
Loading