1. Separate the data and the dropdows
That's a best practice, because you will need to have some auxiliary cells to create the dependency
2. Create a simple data validation dropdown
Do it for the first column in a separate sheet
3. Create the auxiliary cells
Filter the second column with the values you have in the data validation to show the values adjacent to the first column
=FILTER(B2:B,A2:A=Dropdowns!$A$2)

4. Repeat the process with as many columns you have

Here is the sample spreadsheet for you to have an idea how to implement it
https://docs.google.com/spreadsheets/d/1VPsx1hfKuZDifMgrREOvUb4xEIvbVODnKPuRDWZUOF8/edit?usp=sharing
Update
I'm working on a script, I created two functions, at the moment they're not achieving the step you want, but I'm thinking on looping the number of columns as the same way I'm doing it with the rows.
const menuSheet = 'Values';
const dataSheet = 'Data';
const wsValues = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(menuSheet);
const wsData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dataSheet);
const data = wsData.getRange(2, 1, wsData.getLastRow() - 1, 3).getValues();
let firstCol = 1;
let secondCol = 2;
let thirdCol = 3;
/*
function setDropdownDependenciesTest() {
const list = ['a', 'b']
const cell = wsValues.getRange('C2')
setDropdownDependencies(list, cell)
}
*/
function onEdit(e) {
const activeCell = e.range
let val = activeCell.getValue()
let row = activeCell.getRow()
let column = activeCell.getColumn()
let wsName = activeCell.getSheet().getName()
if (wsName === menuSheet && column === firstCol && row > 1) {
applyFirstValidation(val, row)
} else if (wsName == menuSheet && column === secondCol && row > 1) {
applySecondValidation(val, row)
}
}
function applyFirstValidation(val, row) {
if (val === "") {
wsValues.getRange(row, secondCol).clearContent()
wsValues.getRange(row, secondCol).clearDataValidations()
wsValues.getRange(row, thirdCol).clearContent()
wsValues.getRange(row, thirdCol).clearDataValidations()
} else {
wsValues.getRange(row, secondCol).clearContent()
wsValues.getRange(row, secondCol).clearDataValidations()
wsValues.getRange(row, thirdCol).clearContent()
wsValues.getRange(row, thirdCol).clearDataValidations()
let filteredData = data.filter(info => {
return info[0] === val;
})
let listToApply = filteredData.map(info => {
return info[1]
})
let cell = wsValues.getRange(row, secondCol)
setDropdownDependencies(listToApply, cell)
}
}
function applySecondValidation(val, row) {
if (val === "") {
wsValues.getRange(row, thirdCol).clearContent()
wsValues.getRange(row, thirdCol).clearDataValidations()
} else {
wsValues.getRange(row, thirdCol).clearContent()
let firstColValue = wsValues.getRange(row, firstCol).getValue()
let filteredData = data.filter(info => {
return info[0] === firstColValue && info[1] === val;
})
let listToApply = filteredData.map(info => {
return info[2]
})
let cell = wsValues.getRange(row, thirdCol)
setDropdownDependencies(listToApply, cell)
}
}
function setDropdownDependencies(list, cell) {
const rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build()
cell.setDataValidation(rule)
}